Exemple #1
0
        private float[] GetCollisionPointFromCamera(NiNode cameraNode, TESObjectCELL cell, NiAVObject[] ignore)
        {
            float[] source = new float[3];
            float[] target = new float[3];

            var nodePos = cameraNode.WorldTransform.Position;

            source[0] = nodePos.X;
            source[1] = nodePos.Y;
            source[2] = nodePos.Z;

            cameraNode.WorldTransform.Translate(this.AimVectorPointDoubled, this.TargetTeleportPoint);

            target[0] = this.TargetTeleportPoint.X;
            target[1] = this.TargetTeleportPoint.Y;
            target[2] = this.TargetTeleportPoint.Z;

            var ray  = this.DoRayCast(cell, source, target);
            var best = this.GetBestResult(source, ray, ignore, false);

            if (best == null)
            {
                return(target);
            }
            return(best.Position);
        }
Exemple #2
0
 private List <RayCastResult> DoRayCast(TESObjectCELL cell, float[] source, float[] target)
 {
     return(cell.RayCast(new RayCastParameters()
     {
         Begin = source,
         End = target
     }));
 }
Exemple #3
0
        private float QuickRay(float[] source, float[] target, NiAVObject[] ignore, TESObjectCELL cell)
        {
            var ray  = this.DoRayCast(cell, source, target);
            var best = this.GetBestResult(source, ray, ignore, false);

            if (best == null)
            {
                return(-1.0f);
            }

            return(Distance(best.Position, source));
        }
        internal bool CanPlaceGrass(TESObjectCELL cell, TESObjectLAND land, float x, float y, float z)
        {
            if (cell == null)
            {
                return(true);
            }

            // Currently not dealing with this.
            if (cell.IsInterior || !cell.IsAttached)
            {
                return(true);
            }

            var rp = new RayCastParameters();

            rp.Begin = new float[] { x, y, z + this.RayHeight };
            rp.End   = new float[] { x, y, z - this.RayDepth };
            rp.Cell  = cell;
            var rs = TESObjectCELL.RayCast(rp);

            foreach (var r in rs)
            {
                if (r.Fraction >= 1.0f || r.HavokObject == IntPtr.Zero)
                {
                    continue;
                }

                uint  flags = Memory.ReadUInt32(r.HavokObject + 0x2C) & 0x7F;
                ulong mask  = (ulong)1 << (int)flags;
                if ((this.RaycastMask & mask) == 0)
                {
                    continue;
                }

                if (this.Ignore != null && this.IsIgnoredObject(r))
                {
                    continue;
                }

                return(false);
            }

            return(true);
        }
Exemple #5
0
        private bool CheckRay(float[] source, float[] target, NiAVObject[] ignore, TESObjectCELL cell)
        {
            var ray = this.DoRayCast(cell, source, target);

            return(this.GetBestResult(source, ray, ignore, true) == null);
        }
Exemple #6
0
        private bool CalculatePositionFromCollision(float[] colPos, float[] colNormal, ref float[] teleportPos, ref float[] markerPos, NiAVObject[] ignore, TESObjectCELL cell, bool allowWallClimb)
        {
            // Calculate the position.
            var   ntype       = CalculateNormalType(colNormal);
            float tpush       = 1.0f;
            bool  canMoveDown = true;

            switch (ntype)
            {
            case NormalTypes.Up:
                canMoveDown = false;
                break;

            case NormalTypes.Down:
                //tpush = this.Plugin.Settings.PlayerRadius * 2.0f;
                break;

            case NormalTypes.Sideways:
            {
                tpush = this.Plugin.Settings.PlayerRadius;

                var pushed       = PushAway(colPos, colNormal, this.Plugin.Settings.PlayerRadius * 2.0f + 2.0f);
                var pushedSource = PushAway(colPos, colNormal, 0.5f);
                if (!this.CheckRay(pushedSource, pushed, ignore, cell))
                {
                    return(false);
                }
            }
            break;

            case NormalTypes.Diagonal:
                /*if (colNormal[2] >= 0.0f)
                 *  tpush = this.Plugin.Settings.PlayerRadius * 0.66f;
                 * else
                 *  tpush = this.Plugin.Settings.PlayerRadius * 1.33f;*/
                break;
            }
            var tpos = PushAway(colPos, colNormal, tpush);

            // Try to climb a wall.
            bool skipSnap   = false;
            bool skipHeight = false;

            if (allowWallClimb)
            {
                float wallClimb = this.Plugin.Settings.MaxWallClimbHeight;
                if (ntype == NormalTypes.Sideways && wallClimb > 0.0f /* && tpos[2] > (this.SourceMovePoint.X - 40.0f)*/)
                {
                    var srcPos = tpos.ToArray();
                    var dstPos = srcPos.ToArray();
                    dstPos[2] += wallClimb;

                    float dist    = QuickRay(srcPos, dstPos, ignore, cell);
                    float pheight = this.Plugin.Settings.PlayerRadius * 3.0f;
                    if (dist < 0.0f || dist > pheight)
                    {
                        var colNormalRev = colNormal.ToArray();
                        for (int i = 0; i < colNormalRev.Length; i++)
                        {
                            colNormalRev[i] *= -1.0f;
                        }

                        if (dist < 0.0f)
                        {
                            srcPos[2] += wallClimb;
                        }
                        else
                        {
                            srcPos[2] += (dist - 5.0f);
                        }

                        float width = this.Plugin.Settings.WallClimbWidth;
                        dstPos = PushAway(srcPos, colNormalRev, width);
                        if (CheckRay(srcPos, dstPos, ignore, cell))
                        {
                            srcPos     = PushAway(srcPos, colNormalRev, width - this.Plugin.Settings.PlayerRadius);
                            dstPos     = srcPos.ToArray();
                            dstPos[2] -= wallClimb;

                            float dist2 = QuickRay(srcPos, dstPos, ignore, cell);
                            if (dist2 >= 0.0f && (srcPos[2] - dist2) > (tpos[2] - 40.0f))
                            {
                                srcPos[2] -= (dist2 - 1.0f);

                                // Make sure there's enough height for player to exist there.
                                bool climb = true;
                                {
                                    dstPos     = srcPos.ToArray();
                                    dstPos[2] += pheight;

                                    float dist3 = QuickRay(srcPos, dstPos, ignore, cell);
                                    if (dist3 >= 0.0f)
                                    {
                                        float moveDown = pheight - dist3;
                                        dstPos[2] = srcPos[2] - moveDown;
                                        if (!CheckRay(srcPos, dstPos, ignore, cell))
                                        {
                                            climb = false;
                                        }
                                        else
                                        {
                                            srcPos[2] -= moveDown;
                                        }
                                    }
                                }

                                if (climb)
                                {
                                    tpos       = srcPos.ToArray();
                                    skipSnap   = true;
                                    skipHeight = true;
                                }
                            }
                        }
                    }
                }
            }

            // Make sure there's enough height for player to exist there.
            if (!skipHeight)
            {
                float pheight = this.Plugin.Settings.PlayerRadius * 3.0f;
                var   srcPos  = tpos.ToArray();
                var   dstPos  = srcPos.ToArray();
                dstPos[2] += pheight;

                float dist = QuickRay(srcPos, dstPos, ignore, cell);
                if (dist >= 0.0f)
                {
                    if (!canMoveDown)
                    {
                        return(false);
                    }

                    float moveDown = pheight - dist;
                    dstPos[2] = srcPos[2] - moveDown;
                    if (!CheckRay(srcPos, dstPos, ignore, cell))
                    {
                        return(false);
                    }

                    tpos[2] -= moveDown;
                }
            }

            // Try to snap to ground.
            float maxSnap = this.Plugin.Settings.MaxSnapToGroundDistance;

            if (!skipSnap && maxSnap > 0.0f && canMoveDown)
            {
                var srcPos = tpos.ToArray();
                var dstPos = srcPos.ToArray();
                dstPos[2] -= maxSnap;

                float dist = QuickRay(srcPos, dstPos, ignore, cell);
                if (dist >= 0.0f)
                {
                    float reduce = dist - 1.0f;
                    tpos[2] -= reduce;
                }
            }

            teleportPos = tpos;
            float[] mpos = tpos.ToArray();
            mpos[2]  += this.Plugin.Settings.PlayerRadius;
            markerPos = mpos;
            return(true);
        }
Exemple #7
0
        private void UpdateAiming(float diff, float totalTime, PlayerCharacter plr, TESObjectCELL cell)
        {
            if (!this.IsMarkerInWorld)
            {
                return;
            }

            var camera = PlayerCamera.Instance;

            if (camera == null)
            {
                return;
            }

            var cameraNode = camera.Node;

            if (cameraNode == null)
            {
                return;
            }

            NiNode[] playerNode = new NiNode[2]
            {
                plr.GetSkeletonNode(false),
                plr.GetSkeletonNode(true)
            };

            for (int i = 0; i < playerNode.Length; i++)
            {
                if (playerNode[i] == null)
                {
                    return;
                }
            }

            {
                var plrPos = plr.Position;
                this.SourceMovePoint.X = plrPos.X;
                this.SourceMovePoint.Y = plrPos.Y;
                this.SourceMovePoint.Z = plrPos.Z;
            }

            if (camera.State != null && camera.State.Id == TESCameraStates.FirstPerson)
            {
                var plrPos = cameraNode.WorldTransform.Position;
                this.SourceTeleportPoint.X = plrPos.X;
                this.SourceTeleportPoint.Y = plrPos.Y;
                this.SourceTeleportPoint.Z = plrPos.Z;

                cameraNode.WorldTransform.Translate(this.AimVectorPoint, this.TargetTeleportPoint);
            }
            else
            {
                var headNode = playerNode[0].LookupNodeByName("NPC Head [Head]");
                if (headNode != null)
                {
                    var plrPos = headNode.WorldTransform.Position;
                    this.SourceTeleportPoint.X = plrPos.X;
                    this.SourceTeleportPoint.Y = plrPos.Y;
                    this.SourceTeleportPoint.Z = plrPos.Z;
                }
                else
                {
                    var plrPos = plr.Position;
                    this.SourceTeleportPoint.X = plrPos.X;
                    this.SourceTeleportPoint.Y = plrPos.Y;
                    this.SourceTeleportPoint.Z = plrPos.Z + this.Plugin.Settings.PlayerRadius;
                }

                var    cameraCol = this.GetCollisionPointFromCamera(cameraNode, cell, playerNode);
                byte[] data      = Memory.ReadBytes(cameraNode.WorldTransform.Address, MemoryObject.SizeOf <NiTransform>());
                Memory.WriteBytes(this.ThirdPersonTempTransform.Address, data);
                this.TargetTeleportPoint.X = cameraCol[0];
                this.TargetTeleportPoint.Y = cameraCol[1];
                this.TargetTeleportPoint.Z = cameraCol[2];
                this.ThirdPersonTempTransform.LookAt(this.TargetTeleportPoint);
                this.ThirdPersonTempTransform.Translate(this.AimVectorPoint, this.TargetTeleportPoint);
            }

            bool allowWallClimb = true;
            {
                var source = new[] { this.SourceTeleportPoint.X, this.SourceTeleportPoint.Y, this.SourceTeleportPoint.Z };
                var target = new[] { this.TargetTeleportPoint.X, this.TargetTeleportPoint.Y, this.TargetTeleportPoint.Z };
                var ray    = this.DoRayCast(cell, source, target);

                float[] collisionPosition = null;
                float[] collisionNormal   = null;

                var best = this.GetBestResult(source, ray, playerNode, false);
                if (best == null)
                {
                    collisionPosition = target;

                    float[] revNormal = new float[3];
                    float   rx        = target[0] - source[0];
                    float   ry        = target[1] - source[1];
                    float   rz        = target[2] - source[2];
                    float   rd        = (float)Math.Sqrt(rx * rx + ry * ry + rz * rz);
                    if (rd > 0.0f)
                    {
                        rx /= rd;
                        ry /= rd;
                        rz /= rd;
                    }
                    revNormal[0]    = rx * -1.0f;
                    revNormal[1]    = ry * -1.0f;
                    revNormal[2]    = rz * -1.0f;
                    collisionNormal = revNormal;
                    allowWallClimb  = false;
                }
                else
                {
                    collisionPosition = best.Position;
                    collisionNormal   = best.Normal;

                    float nlen = Length(collisionNormal);
                    if (nlen > 0.0f)
                    {
                        collisionNormal[0] /= nlen;
                        collisionNormal[1] /= nlen;
                        collisionNormal[2] /= nlen;
                    }

                    if (!this.Plugin.Settings.AllowLedgeClimbNPC)
                    {
                        var hkObj = best.HavokObject;
                        if (hkObj != IntPtr.Zero)
                        {
                            var layer = (CollisionLayers)(Memory.ReadUInt32(hkObj + 0x2C) & 0x7F);
                            switch (layer)
                            {
                            case CollisionLayers.LivingAndDeadActors:
                            case CollisionLayers.BipedNoCC:
                            case CollisionLayers.CharController:
                                allowWallClimb = false;
                                break;
                            }
                        }
                    }
                }

                float fullDist = Distance(collisionPosition, source);
                float ratioInc = 1.0f;
                if (fullDist > 0.0f)
                {
                    ratioInc = Math.Max(10.0f, this.Plugin.Settings.TeleportIncrementalCheck) / fullDist;
                }
                ratioInc = Math.Max(0.02f, ratioInc);

                float   ratioNow = 1.0f;
                float[] checkPos = new float[3];
                bool    ok       = false;
                while (ratioNow > 0.0f)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        checkPos[i] = (collisionPosition[i] - source[i]) * ratioNow + source[i];
                    }

                    float[] tpPos = null;
                    float[] mrPos = null;
                    if (this.CalculatePositionFromCollision(checkPos, collisionNormal, ref tpPos, ref mrPos, playerNode, cell, allowWallClimb))
                    {
                        this.TargetTeleportPoint.X = tpPos[0];
                        this.TargetTeleportPoint.Y = tpPos[1];
                        this.TargetTeleportPoint.Z = tpPos[2];

                        this.TargetMarkerPoint.X = mrPos[0];
                        this.TargetMarkerPoint.Y = mrPos[1];
                        this.TargetMarkerPoint.Z = mrPos[2];

                        ok = true;
                        break;
                    }

                    ratioNow -= ratioInc;
                }

                if (!ok)
                {
                    this.TargetTeleportPoint.X = this.SourceMovePoint.X;
                    this.TargetTeleportPoint.Y = this.SourceMovePoint.Y;
                    this.TargetTeleportPoint.Z = this.SourceMovePoint.Z;

                    this.TargetMarkerPoint.X = this.SourceMovePoint.X;
                    this.TargetMarkerPoint.Y = this.SourceMovePoint.Y;
                    this.TargetMarkerPoint.Z = this.SourceMovePoint.Z + this.Plugin.Settings.PlayerRadius;
                }
            }

            var mpos = this.Marker.LocalTransform.Position;

            mpos.X = this.TargetMarkerPoint.X;
            mpos.Y = this.TargetMarkerPoint.Y;
            mpos.Z = this.TargetMarkerPoint.Z;

            this.Marker.Update(totalTime);
        }
Exemple #8
0
        internal void Update(float diff, float totalTime)
        {
            NetScriptFramework.SkyrimSE.Main main = NetScriptFramework.SkyrimSE.Main.Instance;
            PlayerCharacter plr   = null;
            TESObjectCELL   cell  = null;
            SpellItem       spell = this.Plugin.Settings.SpellForm;

            if (main == null || main.IsGamePaused || (plr = PlayerCharacter.Instance) == null || (cell = plr.ParentCell) == null)
            {
                this.Reset();
                return;
            }

            this.UpdateHotkey();

            if (this.CurrentDistortionTime < DistortionDurationAfter)
            {
                this.CurrentDistortionTime += diff;
            }

            if (this.Plugin.Settings.AutoLearnSpell && spell != null && !plr.HasSpell(spell))
            {
                plr.AddSpell(spell, true);
            }

            switch (this.State)
            {
            case InternalStates.None:
            {
                var castState = this.GetCurrentCastingState(plr, spell);
                if (castState == MagicCastingStates.Charged)
                {
                    if (!this.CheckCosts(plr, true, false))
                    {
                        if (spell != null)
                        {
                            this.InterruptCast(plr, spell);
                        }
                        if (this.HotkeyState != 0)
                        {
                            this.HotkeyState = -1;
                        }
                        return;
                    }

                    this.State = InternalStates.Aiming;
                    this.AddMarkerToWorld();
                    this.UpdateAiming(diff, totalTime, plr, cell);
                }
            }
            break;

            case InternalStates.Aiming:
            {
                var castState = this.GetCurrentCastingState(plr, spell);
                if (castState != MagicCastingStates.Charged && castState != MagicCastingStates.Released)
                {
                    this.State = InternalStates.None;
                    this.RemoveMarkerFromWorld();
                }
                else
                {
                    if (!this.CheckCosts(plr, true, false))
                    {
                        this.State = InternalStates.None;
                        this.RemoveMarkerFromWorld();
                        if (spell != null)
                        {
                            this.InterruptCast(plr, spell);
                        }
                        if (this.HotkeyState != 0)
                        {
                            this.HotkeyState = -1;
                        }
                        return;
                    }

                    this.UpdateAiming(diff, totalTime, plr, cell);
                }
            }
            break;

            case InternalStates.Fire:
            {
                this.RemoveMarkerFromWorld();

                if (!this.CheckCosts(plr, true, true))
                {
                    this.State = InternalStates.None;
                    if (this.HotkeyState != 0)
                    {
                        this.HotkeyState = -1;
                    }
                    return;
                }

                this.ApplyIMod(plr);
                this.PlaySound(plr);

                float distance = this.TargetTeleportPoint.GetDistance(this.SourceTeleportPoint);
                float speed    = this.Plugin.Settings.TeleportSpeed;
                this.CurrentDistortionTime = 0.0f;
                this.CurrentTeleportTime   = 0.0f;
                float maxDistance = Math.Max(100.0f, this.Plugin.Settings.MaxDistance);
                this.CurrentMaxDistort = MaxTargetDistort * (distance / maxDistance);
                if (speed > 0.0f)
                {
                    if (speed < 1.0f)
                    {
                        speed = 1.0f;
                    }

                    float time = distance / speed;
                    if (time > 0.0f)
                    {
                        this.TimedValues[(int)TimedValueTypes.TeleportDoneRatio] = new List <TimedValue>()
                        {
                            new TimedValue(time, 0, 1)
                        };
                        this.CurrentDistortionTime = -time;
                        this.CurrentTeleportTime   = maxDistance / speed;       // time;
                    }
                }

                this.State = InternalStates.Teleporting;
            }
            break;

            case InternalStates.Teleporting:
            {
                var   tm    = this.TimedValues[(int)TimedValueTypes.TeleportDoneRatio];
                float ratio = 1.0f;
                if (tm != null && tm.Count != 0)
                {
                    tm[0].Update(diff);
                    ratio = tm[0].CurrentValue;
                    if (tm[0].IsFinished)
                    {
                        this.TimedValues[(int)TimedValueTypes.TeleportDoneRatio] = null;
                    }
                }

                this.CurrentTeleportPoint.X = (this.TargetTeleportPoint.X - this.SourceTeleportPoint.X) * ratio + this.SourceTeleportPoint.X;
                this.CurrentTeleportPoint.Y = (this.TargetTeleportPoint.Y - this.SourceTeleportPoint.Y) * ratio + this.SourceTeleportPoint.Y;
                this.CurrentTeleportPoint.Z = (this.TargetTeleportPoint.Z - this.SourceTeleportPoint.Z) * ratio + this.SourceTeleportPoint.Z;

                Memory.InvokeCdecl(this.fn_Actor_SetPosition, plr.Address, this.CurrentTeleportPoint.Address, 1);
                if (ratio >= 1.0f)
                {
                    this.State = InternalStates.None;
                }
            }
            break;
            }
        }
Exemple #9
0
        internal static bool Apply(CameraUpdate update, NiTransform transform, NiPoint3 result)
        {
            init();

            if (update == null || transform == null || result == null)
            {
                return(false);
            }

            if (update.Values.CollisionEnabled.CurrentValue < 0.5)
            {
                return(false);
            }

            var actor = update.Target.Actor;

            if (actor == null)
            {
                return(false);
            }

            var cell = actor.ParentCell;

            if (cell == null)
            {
                return(false);
            }

            float safety = (float)(update.Values.NearClip.CurrentValue + 1.0);

            if (safety < 1.0f)
            {
                safety = 1.0f;
            }

            float safety2 = Math.Max(0.0f, Settings.Instance.CameraCollisionSafety);

            var tpos = transform.Position;

            TempPoint1.CopyFrom(actor.Position);
            TempPoint1.Z = tpos.Z;

            if (safety2 > 0.0f)
            {
                TempSafety.Y = -safety2 * 0.5f;
                TempTransform.CopyFrom(transform);
                TempTransform.Position.CopyFrom(TempPoint1);
                TempTransform.Translate(TempSafety, TempPoint1);
            }

            TempNormal.X = tpos.X - TempPoint1.X;
            TempNormal.Y = tpos.Y - TempPoint1.Y;
            TempNormal.Z = tpos.Z - TempPoint1.Z;

            float len = TempNormal.Length;

            if (len <= 0.0f)
            {
                return(false);
            }

            TempNormal.Normalize(TempNormal);
            TempNormal.Multiply(len + safety + safety2, TempNormal);

            TempPoint2.X = TempPoint1.X + TempNormal.X;
            TempPoint2.Y = TempPoint1.Y + TempNormal.Y;
            TempPoint2.Z = TempPoint1.Z + TempNormal.Z;

            var ls = TESObjectCELL.RayCast(new RayCastParameters()
            {
                Cell  = cell,
                Begin = new float[] { TempPoint1.X, TempPoint1.Y, TempPoint1.Z },
                End   = new float[] { TempPoint2.X, TempPoint2.Y, TempPoint2.Z }
            });

            if (ls == null || ls.Count == 0)
            {
                return(false);
            }

            RayCastResult     best     = null;
            float             bestDist = 0.0f;
            List <NiAVObject> ignore   = new List <NiAVObject>(3);

            {
                var sk = actor.GetSkeletonNode(true);
                if (sk != null)
                {
                    ignore.Add(sk);
                }
            }
            {
                var sk = actor.GetSkeletonNode(false);
                if (sk != null)
                {
                    ignore.Add(sk);
                }
            }
            if (update.CachedMounted)
            {
                var mount = actor.GetMount();
                if (mount != null)
                {
                    var sk = mount.GetSkeletonNode(false);
                    if (sk != null)
                    {
                        ignore.Add(sk);
                    }
                }
            }

            foreach (var r in ls)
            {
                if (!IsValid(r, ignore))
                {
                    continue;
                }

                float dist = r.Fraction;
                if (best == null)
                {
                    best     = r;
                    bestDist = dist;
                }
                else if (dist < bestDist)
                {
                    best     = r;
                    bestDist = dist;
                }
            }

            if (best == null)
            {
                return(false);
            }

            bestDist *= len + safety + safety2;
            bestDist -= safety + safety2;
            bestDist /= len + safety + safety2;

            // Negative is ok!

            result.X = (TempPoint2.X - TempPoint1.X) * bestDist + TempPoint1.X;
            result.Y = (TempPoint2.Y - TempPoint1.Y) * bestDist + TempPoint1.Y;
            result.Z = (TempPoint2.Z - TempPoint1.Z) * bestDist + TempPoint1.Z;

            return(true);
        }
        private TESEffectShader ShouldGlow(TESObjectREFR obj, NiPoint3 pos)
        {
            if (obj == null)
            {
                return(null);
            }

            var           form = obj.BaseForm;
            TESObjectBOOK book = null;

            if (form == null || (book = form as TESObjectBOOK) == null || book.IsRead)
            {
                return(null);
            }

            TESObjectCELL cell = null;

            if (obj.Node == null || (cell = obj.ParentCell) == null)
            {
                return(null);
            }

            var tes = TES.Instance;

            if (tes == null)
            {
                return(null);
            }

            if (!Memory.InvokeCdecl(_is_cell_loaded, tes.Cast <TES>(), cell.Cast <TESObjectCELL>(), 0).ToBool())
            {
                return(null);
            }

            var tracker = Tools.BookTracker.Instance;
            var type    = tracker.GetBookType(book);

            if (!type.HasValue)
            {
#if DEBUG_MSG
                NetScriptFramework.Main.WriteDebugMessage("Warning: " + book.ToString() + " does not have a type in tracker!");
#endif
                return(null);
            }

            int mask = 1 << (int)type.Value;
            if ((this._care_mask & mask) == 0)
            {
                return(null);
            }

            if (obj.Position.GetDistance(pos) > 8000.0f)
            {
                return(null);
            }

            var ls = this._shaders[(int)type.Value];
            if (ls.Count == 0)
            {
                return(null);
            }

            if (ls.Count == 1)
            {
                return(ls[0]);
            }

            return(ls[rnd.Next(0, ls.Count)]);
        }