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

FREE BulletProjectile : 01.0.00 01.0.00

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using UnityEngine;

using Rust;
using ProtoBuf;
using Oxide.Core.Plugins;
using System.Linq;

namespace Oxide.Plugins
{
    [Info("BulletProjectile", "Karuza", "01.0.00")]
    public class BulletProjectile : RustPlugin
    {
        [HookMethod("ShootProjectile")]
        public void ShootProjectile(BasePlayer owner, Vector3 muzzlePos, Vector3 velocity, List<DamageTypeEntry> damageTypeOverrides, string ammoPrefabPathOverride = "")
        {
            ProjectileSystem projectile = new GameObject().AddComponent<ProjectileSystem>();
            projectile.owner = owner;

            if (!string.IsNullOrEmpty(ammoPrefabPathOverride))
                projectile.projectilePrefabPath = ammoPrefabPathOverride;

            projectile.InitializeBullet(muzzlePos);

            if (damageTypeOverrides.Any())
                projectile.damageTypes = damageTypeOverrides;

            projectile.InitializeVelocity(velocity);
            projectile.Launch();
        }

        #region ProjectileSystem
        public class ProjectileSystem : MonoBehaviour
        {
            public float gravityModifier = 1f;
            public float penetrationPower = 1f;
            public MinMax damageDistances = new MinMax(10f, 100f);
            public MinMax damageMultipliers = new MinMax(1f, 0.8f);
            public List<DamageTypeEntry> damageTypes = new List<DamageTypeEntry>();
            [NonSerialized]
            public float integrity = 1f;
            [NonSerialized]
            public float maxDistance = float.PositiveInfinity;
            [NonSerialized]
            public Projectile.Modifier modifier = Projectile.Modifier.Default;
            [Header("Attributes")]
            public Vector3 initialVelocity;
            [Tooltip("This projectile will raycast for this many units, and then become a projectile.This is typically done for bullets.")]
            public float initialDistance;
            [Header("Impact Rules")]
            [Range(0.0f, 1f)]
            public float ricochetChance = 0.1f;
            [Header("Rendering")]
            public ScaleRenderer rendererToScale = null;
            public ScaleRenderer firstPersonRenderer = null;
            [Header("Tumble")]
            public float tumbleSpeed;
            [NonSerialized]
            public BasePlayer owner;
            public string projectilePrefabPath { get; set; } = "assets/prefabs/npc/autoturret/sentrybullet.prefab";
            [NonSerialized]
            public uint seed = 0;
            [NonSerialized]
            public bool clientsideEffect;
            [NonSerialized]
            public bool clientsideAttack;
            [NonSerialized]
            public bool invisible;
            private Vector3 currentVelocity;
            private Vector3 currentPosition;
            private float traveledDistance;
            private float traveledTime;
            private Vector3 sentPosition;
            private Vector3 previousPosition;
            private float previousTraveledTime;
            private bool isRetiring;
            private HitTest hitTest;
            protected static Effect reusableInstance = new Effect();

            public void InitializeBullet(Vector3 location)
            {
                this.initialDistance = 0;
                this.transform.position = this.currentPosition = location;
                invisible = false;
                clientsideEffect = true;
                clientsideAttack = true;
                integrity = 2;
                maxDistance = float.PositiveInfinity;
                damageTypes.Add(new DamageTypeEntry() { type = DamageType.Bullet, amount = 25 });
            }

            public void CalculateDamage(HitInfo info, Projectile.Modifier mod, float scale)
            {
                if (info == null)
                    return;

                float num1 = this.damageMultipliers.Lerp(mod.distanceOffset + mod.distanceScale * this.damageDistances.x, mod.distanceOffset + mod.distanceScale * this.damageDistances.y, info.ProjectileDistance);
                float num2 = scale * (mod.damageOffset + mod.damageScale * num1);

                foreach (DamageTypeEntry damageType in this.damageTypes)
                    info.damageTypes.Add(damageType.type, damageType.amount * num2);
            }

            public bool isAuthoritative
            {
                get
                {
                    if (this.owner != null && this.owner.IsConnected)
                        return true;
                    return false;
                }
            }

            private bool isAlive
            {
                get
                {
                    if (this.integrity > 1.0 / 1000.0 && this.traveledDistance < this.maxDistance)
                        return this.traveledTime < 8.0;
                    return false;
                }
            }

            private void Retire()
            {
                if (this.isRetiring)
                    return;

                this.isRetiring = true;
                this.Cleanup();
            }

            private void Cleanup()
            {
                this.gameObject.BroadcastOnParentDestroying();
                new GameManager(false, true).Retire(this.gameObject);
            }

            public void AdjustVelocity(Vector3 delta)
            {
                this.currentVelocity += delta;
            }

            public void InitializeVelocity(Vector3 overrideVel)
            {
                this.initialVelocity = overrideVel;
                this.currentVelocity = this.initialVelocity;
            }

            protected void OnDisable()
            {
                this.currentVelocity = Vector3.zero;
                this.currentPosition = Vector3.zero;
                this.traveledDistance = 0.0f;
                this.traveledTime = 0.0f;
                this.sentPosition = Vector3.zero;
                this.previousPosition = Vector3.zero;
                this.previousTraveledTime = 0.0f;
                this.isRetiring = false;
                this.owner = null;
                this.seed = 0;
                this.clientsideEffect = false;
                this.clientsideAttack = false;
                this.integrity = 1f;
                this.maxDistance = float.PositiveInfinity;
                this.modifier = Projectile.Modifier.Default;
                this.invisible = false;
            }

            protected void FixedUpdate()
            {
                if (!this.isAlive)
                    return;
                this.UpdateVelocity(Time.fixedDeltaTime);
            }

            protected void Update()
            {
                if (!this.isAlive)
                    this.Retire();
            }

            private void UpdateVelocity(float deltaTime)
            {
                if (this.traveledTime != 0.0f)
                {
                    this.previousPosition = this.currentPosition;
                    this.previousTraveledTime = this.traveledTime;
                }
                this.currentPosition = this.transform.position;
                if (this.traveledTime == 0.0f)
                    this.sentPosition = this.previousPosition = this.currentPosition;

                deltaTime *= Time.timeScale;
                this.DoMovement(deltaTime);
                this.DoVelocityUpdate(deltaTime);

                this.transform.position = this.currentPosition;
                if (this.tumbleSpeed > 0.0f)
                    this.transform.Rotate(Vector3.right, this.tumbleSpeed * deltaTime);
                else
                    this.transform.rotation = Quaternion.LookRotation(this.currentVelocity);
            }

            private void DoVelocityUpdate(float deltaTime)
            {
                this.currentVelocity += Physics.gravity * this.gravityModifier * deltaTime;
                if (!this.isAuthoritative || GamePhysics.LineOfSight(this.sentPosition, this.currentPosition, 2162688, 0.0f))
                    return;
                using (PlayerProjectileUpdate update = Facepunch.Pool.Get<PlayerProjectileUpdate>())
                {
                    update.curPosition = this.previousPosition;
                    update.travelTime = this.previousTraveledTime;

                    this.owner.ClientRPC<PlayerProjectileUpdate>(this.owner.Connection, "OnProjectileUpdate", update);
                    this.sentPosition = this.previousPosition;
                }
            }

            private void DoMovement(float deltaTime)
            {
                Vector3 vector3_1 = this.currentVelocity * deltaTime;
                float magnitude1 = vector3_1.magnitude;
                float num1 = 1f / magnitude1;
                Vector3 direction = vector3_1 * num1;
                bool flag = false;
                Vector3 vPosB = this.currentPosition + direction * magnitude1;

                float num2 = this.traveledTime + deltaTime;
                if (this.hitTest == null)
                    this.hitTest = new HitTest();
                else
                    this.hitTest.Clear();
                this.hitTest.AttackRay = new Ray(this.currentPosition, direction);
                this.hitTest.MaxDistance = magnitude1;
                this.hitTest.ignoreEntity = (BaseEntity)this.owner;
                this.hitTest.Radius = 0.0f;
                this.hitTest.Forgiveness = 1;
                this.hitTest.type = this.isAuthoritative ? HitTest.Type.Projectile : HitTest.Type.ProjectileEffect;

                List<TraceInfo> list = Facepunch.Pool.GetList<TraceInfo>();
                GameTrace.TraceAll(this.hitTest, list);
                
                for (int index = 0; index < list.Count && this.isAlive && !flag; index++)
                {
                    if (list[index].valid)
                    {
                        list[index].UpdateHitTest(this.hitTest);
                        Vector3 vector3_2 = this.hitTest.HitPointWorld();
                        Vector3 normal = this.hitTest.HitNormalWorld();

                        float magnitude2 = (vector3_2 - this.currentPosition).magnitude;
                        float num3 = magnitude2 * num1 * deltaTime;
                        this.traveledDistance += magnitude2;
                        this.traveledTime += num3;

                        this.currentPosition = vector3_2;
                        if (this.DoRicochet(this.hitTest, vector3_2, normal) || this.DoHit(this.hitTest, vector3_2, normal))
                            flag = true;
                    }
                }

                Facepunch.Pool.FreeList<TraceInfo>(ref list);
                if (!this.isAlive)
                    return;
                if (flag && this.traveledTime < num2)
                {
                    this.DoMovement(num2 - this.traveledTime);
                }
                else
                {
                    float magnitude2 = (vPosB - this.currentPosition).magnitude;
                    this.traveledDistance += magnitude2;
                    this.traveledTime += magnitude2 * num1 * deltaTime;
                    this.currentPosition = vPosB;
                }
            }

            private bool DoWaterHit(ref HitTest test, Vector3 targetPosition)
            {
                float height = TerrainMeta.WaterMap.GetHeight(targetPosition);
                if ((double)height < (double)targetPosition.y)
                    return false;
                Vector3 point = targetPosition;
                point.y = height;
                Vector3 normal = TerrainMeta.WaterMap.GetNormal(targetPosition);
                test.DidHit = true;
                test.HitEntity = null;
                test.HitDistance = Vector3.Distance(test.AttackRay.origin, targetPosition);
                test.HitMaterial = "Water";
                test.HitPart = 0U;
                test.HitTransform = null;
                test.HitPoint = point;
                test.HitNormal = normal;
                test.collider = null;
                test.gameObject = null;
                this.DoHit(test, point, normal);
                this.integrity = 0.0f;
                return true;
            }

            private bool DoRicochet(HitTest test, Vector3 point, Vector3 normal)
            {
                Vector3 currentPosition = this.currentPosition;
                Vector3 currentVelocity = this.currentVelocity;
                bool flag = false;
                if ((!(test.HitEntity != null) || !(test.HitEntity is BaseCombatEntity)) && (this.ricochetChance > 0.0f && UnityEngine.Random.Range(1, int.MaxValue) <= this.ricochetChance) && !Projectile.IsWaterMaterial(test.HitMaterial))
                    flag = this.Reflect(ref this.seed, point, normal);
                if (flag)
                {
                    if (this.isAuthoritative)
                    {
                        using (PlayerProjectileRicochet ricochet = Facepunch.Pool.Get<PlayerProjectileRicochet>())
                        {
                            ricochet.hitPosition = currentPosition;
                            ricochet.inVelocity = currentVelocity;
                            ricochet.outVelocity = this.currentVelocity;
                            ricochet.hitNormal = normal;
                            ricochet.travelTime = this.traveledTime;

                            this.owner.ClientRPC<PlayerProjectileRicochet>(this.owner.Connection, "OnProjectileRicochet", ricochet);
                            this.sentPosition = this.currentPosition;
                        }
                    }
                }
                return flag;
            }

            public static Attack BuildAttackMessage(HitTest test)
            {
                uint hitBone = 0;
                uint hitMaterialId = 0;
                if (test.HitTransform && test.HitEntity && test.HitEntity.transform != test.HitTransform)
                {
                    hitBone = StringPool.Get(test.HitTransform.name);
                }
                if (!string.IsNullOrEmpty(test.HitMaterial))
                {
                    hitMaterialId = StringPool.Get(test.HitMaterial);
                }
                Attack attack = Facepunch.Pool.Get<Attack>();
                attack.pointStart = test.AttackRay.origin;
                attack.pointEnd = test.AttackRay.origin + test.AttackRay.direction * test.MaxDistance;
                attack.hitMaterialID = hitMaterialId;
                if (test.DidHit)
                {
                    if (test.HitEntity.IsValid())
                    {
                        Transform transform = test.HitTransform;
                        if (!transform)
                            transform = test.HitEntity.transform;
                        attack.hitID = test.HitEntity.net.ID;
                        attack.hitBone = hitBone;
                        attack.hitPartID = test.HitPart;
                        attack.hitPositionWorld = transform.localToWorldMatrix.MultiplyPoint(test.HitPoint);
                        attack.hitPositionLocal = test.HitPoint;
                        attack.hitNormalWorld = transform.localToWorldMatrix.MultiplyVector(test.HitNormal);
                        attack.hitNormalLocal = test.HitNormal;
                    }
                    else
                    {
                        attack.hitID = 0U;
                        attack.hitBone = 0U;
                        attack.hitPositionWorld = test.HitPoint;
                        attack.hitPositionLocal = test.HitPoint;
                        attack.hitNormalWorld = test.HitNormal;
                        attack.hitNormalLocal = test.HitNormal;
                    }
                }
                return attack;
            }


            private bool DoHit(HitTest test, Vector3 point, Vector3 normal)
            {
                bool flag = false;
                using (PlayerProjectileAttack attack = Facepunch.Pool.Get<PlayerProjectileAttack>())
                {
                    attack.playerAttack = Facepunch.Pool.Get<PlayerAttack>();
                    attack.playerAttack.attack = BuildAttackMessage(test);
                    HitInfo info = new HitInfo();
                    LoadFromAttack(info, attack.playerAttack.attack, false);
                    info.Initiator = this.owner;
                    info.ProjectileDistance = this.traveledDistance;
                    info.ProjectileVelocity = this.currentVelocity;
                    info.IsPredicting = true;
                    info.DoDecals = true;
                    this.CalculateDamage(info, this.modifier, this.integrity);
                    if (object.ReferenceEquals(info.HitEntity, null) && Projectile.IsWaterMaterial(test.HitMaterial))
                    {
                        this.currentVelocity *= 0.1f;
                        this.currentPosition += this.currentVelocity.normalized * (1f / 1000f);
                        this.integrity = Mathf.Clamp01(this.integrity - 0.1f);
                        flag = true;
                    }
                    else if (this.penetrationPower <= 0.0f || object.ReferenceEquals(info.HitEntity, null))
                    {
                        this.integrity = 0.0f;
                    }
                    else
                    {
                        float resistance = info.HitEntity.PenetrationResistance(info) / this.penetrationPower;
                        flag = this.Refract(ref this.seed, point, normal, resistance);
                        this.integrity = Mathf.Clamp01(this.integrity - resistance);
                        if (info.HitEntity is BasePlayer)
                            info.HitMaterial = StringPool.Get("Flesh");
                    }
                    if (this.isAuthoritative)
                    {
                        attack.hitVelocity = this.currentVelocity;
                        attack.hitDistance = this.traveledDistance;
                        attack.travelTime = this.traveledTime;

                        this.owner.ClientRPC<PlayerProjectileAttack>(this.owner.Connection, "OnProjectileAttack", attack);
                        this.sentPosition = this.currentPosition;
                    }
                    if (this.clientsideAttack && info.HitEntity != null)
                        info.HitEntity.OnAttacked(info);
                    if (this.clientsideEffect)
                        Effect.server.ImpactEffect(info);
                }

                return flag;
            }

            public static void LoadFromAttack(HitInfo info, Attack attack, bool serverSide)
            {
                info.HitEntity = null;
                info.PointStart = attack.pointStart;
                info.PointEnd = attack.pointEnd;
                if (attack.hitID > 0U)
                {
                    info.DidHit = true;
                    if (!serverSide)
                        info.HitEntity = BaseNetworkable.serverEntities.Find(attack.hitID) as BaseEntity;
                    if (info.HitEntity != null)
                    {
                        info.HitBone = attack.hitBone;
                        info.HitPart = attack.hitPartID;
                    }
                }
                info.DidHit = true;
                info.HitPositionLocal = attack.hitPositionLocal;
                info.HitPositionWorld = attack.hitPositionWorld;
                info.HitNormalLocal = attack.hitNormalLocal.normalized;
                info.HitNormalWorld = attack.hitNormalWorld.normalized;
                info.HitMaterial = attack.hitMaterialID;
            }

            private bool Reflect(ref uint seed, Vector3 point, Vector3 normal)
            {
                bool flag = false;
                if (this.currentVelocity.magnitude > 50.0f)
                {
                    float velocityMod = Mathf.Clamp01((float)(1.0 - (90f - Vector3.Angle(-this.currentVelocity, normal)) / 30.0)) * 0.8f;
                    if (velocityMod > 0.0f)
                    {
                        this.currentVelocity = Vector3.Reflect(this.currentVelocity, (Quaternion.LookRotation(normal) * this.RandomRotation(ref seed, 10f) * Vector3.forward).normalized) * velocityMod;
                        this.currentPosition += this.currentVelocity.normalized * (1f / 1000f);
                        flag = true;
                    }
                }
                return flag;
            }

            private bool Refract(ref uint seed, Vector3 point, Vector3 normal, float resistance)
            {
                bool flag = false;
                if ((double)resistance < 1.0)
                {
                    float num = Mathf.Lerp(1f, 0.5f, resistance);
                    if (num > 0.0f)
                    {
                        this.currentVelocity = this.Refract(this.currentVelocity, (Quaternion.LookRotation(normal) * this.RandomRotation(ref seed, 10f) * Vector3.forward).normalized, resistance) * num;
                        this.currentPosition += this.currentVelocity.normalized * (1f / 1000f);
                        flag = true;
                    }
                }
                return flag;
            }

            private Vector3 Refract(Vector3 v, Vector3 n, float f)
            {
                float magnitude = v.magnitude;
                return Vector3.Slerp(v / magnitude, -n, f) * magnitude;
            }

            private Quaternion RandomRotation(ref uint seed, float range)
            {
                Xorshift(ref seed);
                float x = seed * (-range - range);
                Xorshift(ref seed);
                float y = seed * (-range - range);
                Xorshift(ref seed);
                float z = seed * (-range - range);
                return Quaternion.Euler(x, y, z);
            }

            public static uint Xorshift(ref uint x)
            {
                x ^= x << 13;
                x ^= x >> 17;
                x ^= x << 5;
                return x;
            }

            internal void Launch()
            {
                Effect reusableInstance = ProjectileSystem.reusableInstance;
                reusableInstance.Clear();
                reusableInstance.Init(Effect.Type.Projectile, this.currentPosition, this.currentVelocity, null);
                reusableInstance.scale = 5f;
                reusableInstance.pooledString = projectilePrefabPath;
                EffectNetwork.Send(reusableInstance);

                while (this.isAlive && (this.traveledDistance < this.initialDistance || this.traveledTime < 0.100000001490116f))
                    this.UpdateVelocity(Time.fixedDeltaTime);
            }
        }

        public static class GameTrace
        {
            public static void TraceAll(HitTest test, List<TraceInfo> traces, int layerMask = -5)
            {
                List<RaycastHit> list = Facepunch.Pool.GetList<RaycastHit>();
                Vector3 origin = test.AttackRay.origin;
                Vector3 direction = test.AttackRay.direction;
                float maxDistance = test.MaxDistance;
                float radius = test.Radius;
                if ((layerMask & 16384) != 0)
                {
                    layerMask &= -16385;
                    GamePhysics.TraceAllUnordered(new Ray(origin - direction * 5f, direction), radius, list, maxDistance + 5f, 16384, QueryTriggerInteraction.UseGlobal);
                    for (int index = 0; index < list.Count; index++)
                    {
                        RaycastHit raycastHit = list[index];
                        raycastHit.distance -= 5f;
                        list[index] = raycastHit;
                    }
                }
                GamePhysics.TraceAllUnordered(new Ray(origin, direction), radius, list, maxDistance, layerMask, QueryTriggerInteraction.UseGlobal);
                for (int index = 0; index < list.Count; index++)
                {
                    RaycastHit hit = list[index];
                    Collider collider = hit.GetCollider();

                    if (!collider.isTrigger)
                    {
                        ColliderInfo component1 = collider.GetComponent<ColliderInfo>();
                        if (object.ReferenceEquals(component1, null) || component1.Filter(test))
                        {
                            if (hit.distance > 0.0f)
                            {
                                TraceInfo traceInfo = new TraceInfo()
                                {
                                    valid = true,
                                    distance = hit.distance,
                                    partID = 0,
                                    point = hit.point,
                                    normal = hit.normal,
                                    collider = collider,
                                    material = collider.GetMaterialAt(hit.point),
                                    entity = collider.gameObject.ToBaseEntity()
                                };
                                traceInfo.bone = GetTransform(collider.transform, hit.point, traceInfo.entity);

                                if (object.ReferenceEquals(traceInfo.entity, null) || traceInfo.entity != test.ignoreEntity)
                                    traces.Add(traceInfo);
                            }
                        }
                    }
                }
                traces.Sort((a, b) => a.distance.CompareTo(b.distance));
                Facepunch.Pool.FreeList<RaycastHit>(ref list);
            }

            public static Transform GetTransform(Transform bone, Vector3 position, BaseEntity entity)
            {
                if (bone.gameObject.GetComponentInParent<Model>() != null)
                    return bone;
                if (object.ReferenceEquals(entity, null))
                    return null;
                if (entity.model && entity.model.rootBone)
                    return entity.model.FindClosestBone(position);
                return entity.transform;
            }
        }

        public struct TraceInfo
        {
            public bool valid;
            public float distance;
            public BaseEntity entity;
            public Vector3 point;
            public Vector3 normal;
            public Transform bone;
            public PhysicMaterial material;
            public uint partID;
            public Collider collider;
            
            public void UpdateHitTest(HitTest test)
            {
                test.DidHit = true;
                test.HitEntity = this.entity;
                test.HitDistance = this.distance;
                test.HitMaterial = this.material != null ? this.material.GetName() : "generic";
                test.HitPart = this.partID;
                test.HitTransform = this.bone;
                test.HitPoint = this.point;
                test.HitNormal = this.normal;
                test.collider = this.collider;
                test.gameObject = this.collider ? this.collider.gameObject : test.HitTransform.gameObject;
                if (test.HitTransform == null)
                    return;
                test.HitPoint = test.HitTransform.InverseTransformPoint(this.point);
                test.HitNormal = test.HitTransform.InverseTransformDirection(this.normal);
            }
        }
        #endregion

    }
}

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