Beispiel #1
0
        public override void Init(bool affectSubImmediately)
        {
            spawnPos = Level.Loaded.GetRandomItemPos(
                (Rand.Value(Rand.RandSync.Server) < 0.5f) ? Level.PositionType.MainPath : Level.PositionType.Cave | Level.PositionType.Ruin,
                500.0f, 10000.0f, 30.0f);

            spawnPending = true;
        }
Beispiel #2
0
 public AttackResult AddDamage(Vector2 simPosition, IEnumerable <Affliction> afflictions, bool playSound)
 {
     appliedDamageModifiers.Clear();
     afflictionsCopy.Clear();
     foreach (var affliction in afflictions)
     {
         var   newAffliction = affliction;
         float random        = Rand.Value(Rand.RandSync.Unsynced);
         if (random > affliction.Probability)
         {
             continue;
         }
         bool applyAffliction = true;
         foreach (DamageModifier damageModifier in DamageModifiers)
         {
             if (!damageModifier.MatchesAffliction(affliction))
             {
                 continue;
             }
             if (random > affliction.Probability * damageModifier.ProbabilityMultiplier)
             {
                 applyAffliction = false;
                 continue;
             }
             if (SectorHit(damageModifier.ArmorSectorInRadians, simPosition))
             {
                 newAffliction = affliction.CreateMultiplied(damageModifier.DamageMultiplier);
                 appliedDamageModifiers.Add(damageModifier);
             }
         }
         foreach (WearableSprite wearable in wearingItems)
         {
             foreach (DamageModifier damageModifier in wearable.WearableComponent.DamageModifiers)
             {
                 if (!damageModifier.MatchesAffliction(affliction))
                 {
                     continue;
                 }
                 if (random > affliction.Probability * damageModifier.ProbabilityMultiplier)
                 {
                     applyAffliction = false;
                     continue;
                 }
                 if (SectorHit(damageModifier.ArmorSectorInRadians, simPosition))
                 {
                     newAffliction = affliction.CreateMultiplied(damageModifier.DamageMultiplier);
                     appliedDamageModifiers.Add(damageModifier);
                 }
             }
         }
         if (applyAffliction)
         {
             afflictionsCopy.Add(newAffliction);
         }
     }
     AddDamageProjSpecific(afflictionsCopy, playSound, appliedDamageModifiers);
     return(new AttackResult(afflictionsCopy, this, appliedDamageModifiers));
 }
Beispiel #3
0
        public void CreateAutonomousObjectives()
        {
            if (character.IsDead)
            {
#if DEBUG
                DebugConsole.ThrowError("Attempted to create autonomous orders for a dead character");
#else
                return;
#endif
            }
            foreach (var delayedObjective in DelayedObjectives)
            {
                CoroutineManager.StopCoroutines(delayedObjective.Value);
            }
            DelayedObjectives.Clear();
            Objectives.Clear();
            FailedAutonomousObjectives = false;
            AddObjective(new AIObjectiveFindSafety(character, this));
            AddObjective(new AIObjectiveIdle(character, this));
            int objectiveCount = Objectives.Count;
            foreach (var autonomousObjective in character.Info.Job.Prefab.AutonomousObjectives)
            {
                var orderPrefab = Order.GetPrefab(autonomousObjective.identifier);
                if (orderPrefab == null)
                {
                    throw new Exception($"Could not find a matching prefab by the identifier: '{autonomousObjective.identifier}'");
                }
                Item item = null;
                if (orderPrefab.MustSetTarget)
                {
                    item = orderPrefab.GetMatchingItems(character.Submarine, mustBelongToPlayerSub: false, requiredTeam: character.Info.TeamID, interactableFor: character)?.GetRandom();
                }
                var order = new Order(orderPrefab, item ?? character.CurrentHull as Entity, orderPrefab.GetTargetItemComponent(item), orderGiver: character);
                if (order == null)
                {
                    continue;
                }
                if (autonomousObjective.ignoreAtOutpost && Level.IsLoadedOutpost && character.TeamID != CharacterTeamType.FriendlyNPC)
                {
                    if (Submarine.MainSub != null && Submarine.MainSub.DockedTo.None(s => s.TeamID != CharacterTeamType.FriendlyNPC && s.TeamID != character.TeamID))
                    {
                        continue;
                    }
                }
                var objective = CreateObjective(order, autonomousObjective.option, character, isAutonomous: true, autonomousObjective.priorityModifier);
                if (objective != null && objective.CanBeCompleted)
                {
                    AddObjective(objective, delay: Rand.Value() / 2);
                    objectiveCount++;
                }
            }
            _waitTimer = Math.Max(_waitTimer, Rand.Range(0.5f, 1f) * objectiveCount);
        }
Beispiel #4
0
 private void ApplyDamage(float deltaTime, bool applyForce)
 {
     foreach (Limb limb in character.AnimController.Limbs)
     {
         float random = Rand.Value(Rand.RandSync.Server);
         huskInfection.Clear();
         huskInfection.Add(AfflictionPrefab.InternalDamage.Instantiate(random * 10 * deltaTime / character.AnimController.Limbs.Length));
         character.LastDamageSource = null;
         float force = applyForce ? random * 0.5f * limb.Mass : 0;
         character.DamageLimb(limb.WorldPosition, limb, huskInfection, 0, false, force);
     }
 }
        private static bool SpawnItem(ItemPrefab itemPrefab, List <ItemContainer> containers, KeyValuePair <ItemContainer, PreferredContainer> validContainer)
        {
            bool success = false;

            if (Rand.Value(Rand.RandSync.Server) > validContainer.Value.SpawnProbability)
            {
                return(false);
            }
            // Don't add dangerously reactive materials in thalamus wrecks
            if (validContainer.Key.Item.Submarine.WreckAI != null && itemPrefab.Tags.Contains("explodesinwater"))
            {
                return(false);
            }
            int amount = Rand.Range(validContainer.Value.MinAmount, validContainer.Value.MaxAmount + 1, Rand.RandSync.Server);

            for (int i = 0; i < amount; i++)
            {
                if (validContainer.Key.Inventory.IsFull(takeStacksIntoAccount: true))
                {
                    containers.Remove(validContainer.Key);
                    break;
                }
                if (!validContainer.Key.Inventory.CanBePut(itemPrefab))
                {
                    break;
                }
                var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine)
                {
                    SpawnedInOutpost       = validContainer.Key.Item.SpawnedInOutpost,
                    AllowStealing          = validContainer.Key.Item.AllowStealing,
                    OriginalModuleIndex    = validContainer.Key.Item.OriginalModuleIndex,
                    OriginalContainerIndex =
                        Item.ItemList.Where(it => it.Submarine == validContainer.Key.Item.Submarine && it.OriginalModuleIndex == validContainer.Key.Item.OriginalModuleIndex).ToList().IndexOf(validContainer.Key.Item)
                };
                foreach (WifiComponent wifiComponent in item.GetComponents <WifiComponent>())
                {
                    wifiComponent.TeamID = validContainer.Key.Item.Submarine.TeamID;
                }
                spawnedItems.Add(item);
                validContainer.Key.Inventory.TryPutItem(item, null, createNetworkEvent: false);
                containers.AddRange(item.GetComponents <ItemContainer>());
                success = true;
            }
            return(success);
        }
Beispiel #6
0
        private void ApplyDamage(float deltaTime, bool applyForce)
        {
            int limbCount = character.AnimController.Limbs.Count(l => !l.ignoreCollisions && !l.IsSevered);

            foreach (Limb limb in character.AnimController.Limbs)
            {
                if (limb.IsSevered)
                {
                    continue;
                }
                float random = Rand.Value();
                huskInfection.Clear();
                huskInfection.Add(AfflictionPrefab.InternalDamage.Instantiate(random * 10 * deltaTime / limbCount));
                character.LastDamageSource = null;
                float force = applyForce ? random * 0.5f * limb.Mass : 0;
                character.DamageLimb(limb.WorldPosition, limb, huskInfection, 0, false, force);
            }
        }
Beispiel #7
0
        public void CreateAutonomousObjectives()
        {
            foreach (var delayedObjective in DelayedObjectives)
            {
                CoroutineManager.StopCoroutines(delayedObjective.Value);
            }
            DelayedObjectives.Clear();
            Objectives.Clear();
            AddObjective(new AIObjectiveFindSafety(character, this));
            AddObjective(new AIObjectiveIdle(character, this));
            int objectiveCount = Objectives.Count;

            foreach (var automaticOrder in character.Info.Job.Prefab.AutomaticOrders)
            {
                var orderPrefab = Order.GetPrefab(automaticOrder.identifier);
                if (orderPrefab == null)
                {
                    throw new Exception($"Could not find a matching prefab by the identifier: '{automaticOrder.identifier}'");
                }
                // TODO: Similar code is used in CrewManager:815-> DRY
                var matchingItems = orderPrefab.ItemIdentifiers.Any() ?
                                    Item.ItemList.FindAll(it => orderPrefab.ItemIdentifiers.Contains(it.Prefab.Identifier) || it.HasTag(orderPrefab.ItemIdentifiers)) :
                                    Item.ItemList.FindAll(it => it.Components.Any(ic => ic.GetType() == orderPrefab.ItemComponentType));
                matchingItems.RemoveAll(it => it.Submarine != character.Submarine);
                var item  = matchingItems.GetRandom();
                var order = new Order(
                    orderPrefab,
                    item ?? character.CurrentHull as Entity,
                    item?.Components.FirstOrDefault(ic => ic.GetType() == orderPrefab.ItemComponentType),
                    orderGiver: character);
                if (order == null)
                {
                    continue;
                }
                var objective = CreateObjective(order, automaticOrder.option, character, automaticOrder.priorityModifier);
                if (objective != null)
                {
                    AddObjective(objective, delay: Rand.Value() / 2);
                    objectiveCount++;
                }
            }
            _waitTimer = Math.Max(_waitTimer, Rand.Range(0.5f, 1f) * objectiveCount);
        }
        public void CreateAutonomousObjectives()
        {
            if (character.IsDead)
            {
#if DEBUG
                DebugConsole.ThrowError("Attempted to create autonomous orders for a dead character");
#else
                return;
#endif
            }
            foreach (var delayedObjective in DelayedObjectives)
            {
                CoroutineManager.StopCoroutines(delayedObjective.Value);
            }
            DelayedObjectives.Clear();
            Objectives.Clear();
            AddObjective(new AIObjectiveFindSafety(character, this));
            AddObjective(new AIObjectiveIdle(character, this));
            int objectiveCount = Objectives.Count;
            foreach (var autonomousObjective in character.Info.Job.Prefab.AutonomousObjective)
            {
                var orderPrefab = Order.GetPrefab(autonomousObjective.identifier);
                if (orderPrefab == null)
                {
                    throw new Exception($"Could not find a matching prefab by the identifier: '{autonomousObjective.identifier}'");
                }
                var item  = orderPrefab.MustSetTarget ? orderPrefab.GetMatchingItems(character.Submarine, false)?.GetRandom() : null;
                var order = new Order(orderPrefab, item ?? character.CurrentHull as Entity,
                                      item?.Components.FirstOrDefault(ic => ic.GetType() == orderPrefab.ItemComponentType), orderGiver: character);
                if (order == null)
                {
                    continue;
                }
                var objective = CreateObjective(order, autonomousObjective.option, character, isAutonomous: true, autonomousObjective.priorityModifier);
                if (objective != null && objective.CanBeCompleted)
                {
                    AddObjective(objective, delay: Rand.Value() / 2);
                    objectiveCount++;
                }
            }
            _waitTimer = Math.Max(_waitTimer, Rand.Range(0.5f, 1f) * objectiveCount);
        }
Beispiel #9
0
        private static bool SpawnItem(ItemPrefab itemPrefab, List <ItemContainer> containers, KeyValuePair <ItemContainer, PreferredContainer> validContainer)
        {
            bool success = false;

            if (Rand.Value() > validContainer.Value.SpawnProbability)
            {
                return(false);
            }
            // Don't add dangerously reactive materials in thalamus wrecks
            if (validContainer.Key.Item.Submarine.WreckAI != null && itemPrefab.Tags.Contains("explodesinwater"))
            {
                return(false);
            }
            int amount = Rand.Range(validContainer.Value.MinAmount, validContainer.Value.MaxAmount + 1);

            for (int i = 0; i < amount; i++)
            {
                if (validContainer.Key.Inventory.IsFull())
                {
                    containers.Remove(validContainer.Key);
                    break;
                }

                var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine);
                foreach (WifiComponent wifiComponent in item.GetComponents <WifiComponent>())
                {
                    wifiComponent.TeamID = validContainer.Key.Item.Submarine.TeamID;
                }
                spawnedItems.Add(item);
#if SERVER
                Entity.Spawner.CreateNetworkEvent(item, remove: false);
#endif
                validContainer.Key.Inventory.TryPutItem(item, null);
                containers.AddRange(item.GetComponents <ItemContainer>());
                success = true;
            }
            return(success);
        }
Beispiel #10
0
        private void FindSpawnPosition(bool affectSubImmediately)
        {
            if (disallowed)
            {
                return;
            }

            spawnPos = Vector2.Zero;
            var availablePositions = GetAvailableSpawnPositions();
            var chosenPosition     = new Level.InterestingPosition(Point.Zero, Level.PositionType.MainPath, isValid: false);
            var removedPositions   = new List <Level.InterestingPosition>();

            foreach (var position in availablePositions)
            {
                if (Rand.Value(Rand.RandSync.Server) > prefab.SpawnProbability)
                {
                    removedPositions.Add(position);
                }
            }
            removedPositions.ForEach(p => availablePositions.Remove(p));
            bool isSubOrWreck = spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Wreck;

            if (affectSubImmediately && !isSubOrWreck)
            {
                if (availablePositions.None())
                {
                    //no suitable position found, disable the event
                    Finished();
                    return;
                }
                float closestDist = float.PositiveInfinity;
                //find the closest spawnposition that isn't too close to any of the subs
                foreach (var position in availablePositions)
                {
                    Vector2 pos  = position.Position.ToVector2();
                    float   dist = Vector2.DistanceSquared(pos, Submarine.MainSub.WorldPosition);
                    foreach (Submarine sub in Submarine.Loaded)
                    {
                        if (sub.Info.Type != SubmarineInfo.SubmarineType.Player)
                        {
                            continue;
                        }
                        float minDistToSub = GetMinDistanceToSub(sub);
                        if (dist > minDistToSub * minDistToSub && dist < closestDist)
                        {
                            closestDist    = dist;
                            chosenPosition = position;
                        }
                    }
                }
                //only found a spawnpos that's very far from the sub, pick one that's closer
                //and wait for the sub to move further before spawning
                if (closestDist > 15000.0f * 15000.0f)
                {
                    foreach (var position in availablePositions)
                    {
                        float dist = Vector2.DistanceSquared(position.Position.ToVector2(), Submarine.MainSub.WorldPosition);
                        if (dist < closestDist)
                        {
                            closestDist    = dist;
                            chosenPosition = position;
                        }
                    }
                }
            }
            else
            {
                if (!isSubOrWreck)
                {
                    float minDistance = 20000;
                    availablePositions.RemoveAll(p => Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, p.Position.ToVector2()) < minDistance * minDistance);
                }
                if (availablePositions.None())
                {
                    //no suitable position found, disable the event
                    Finished();
                    return;
                }
                chosenPosition = availablePositions.GetRandom();
            }
            if (chosenPosition.IsValid)
            {
                spawnPos = chosenPosition.Position.ToVector2();
                if (chosenPosition.Submarine != null || chosenPosition.Ruin != null)
                {
                    var spawnPoint = WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine, ruin: chosenPosition.Ruin, useSyncedRand: false);
                    if (spawnPoint != null)
                    {
                        System.Diagnostics.Debug.Assert(spawnPoint.Submarine == chosenPosition.Submarine);
                        System.Diagnostics.Debug.Assert(spawnPoint.ParentRuin == chosenPosition.Ruin);
                        spawnPos = spawnPoint.WorldPosition;
                    }
                }
                else if (chosenPosition.PositionType == Level.PositionType.MainPath && offset > 0)
                {
                    Vector2 dir;
                    var     waypoints       = WayPoint.WayPointList.FindAll(wp => wp.Submarine == null);
                    var     nearestWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, spawnPos.Value)).FirstOrDefault();
                    if (nearestWaypoint != null)
                    {
                        int currentIndex = waypoints.IndexOf(nearestWaypoint);
                        var nextWaypoint = waypoints[Math.Min(currentIndex + 20, waypoints.Count - 1)];
                        dir = Vector2.Normalize(nextWaypoint.WorldPosition - nearestWaypoint.WorldPosition);
                    }
                    else
                    {
                        dir = new Vector2(1, Rand.Range(-1, 1));
                    }
                    Vector2 targetPos      = spawnPos.Value + dir * offset;
                    var     targetWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, targetPos)).FirstOrDefault();
                    if (targetWaypoint != null)
                    {
                        spawnPos = targetWaypoint.WorldPosition;
                    }
                }
                spawnPending = true;
            }
        }
Beispiel #11
0
        public void SetCoolDown()
        {
            float randomFraction = CoolDown * CoolDownRandomFactor;

            CoolDownTimer          = CoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value(Rand.RandSync.Server));
            randomFraction         = SecondaryCoolDown * CoolDownRandomFactor;
            SecondaryCoolDownTimer = SecondaryCoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value(Rand.RandSync.Server));
        }
Beispiel #12
0
        public override void DragCharacter(Character target, float deltaTime)
        {
            if (target == null)
            {
                return;
            }
            Limb mouthLimb = GetLimb(LimbType.Head);

            if (mouthLimb == null)
            {
                return;
            }

            if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
            {
                //stop dragging if there's something between the pull limb and the target
                Vector2 sourceSimPos = SimplePhysicsEnabled ? character.SimPosition : mouthLimb.SimPosition;
                Vector2 targetSimPos = target.SimPosition;
                if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
                {
                    targetSimPos -= character.Submarine.SimPosition;
                }
                else if (character.Submarine == null && character.SelectedCharacter.Submarine != null)
                {
                    sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
                }
                var body = Submarine.CheckVisibility(sourceSimPos, targetSimPos, ignoreSubs: true);
                if (body != null)
                {
                    character.DeselectCharacter();
                    return;
                }
            }

            float dmg      = character.Params.EatingSpeed;
            float eatSpeed = dmg / ((float)Math.Sqrt(Math.Max(target.Mass, 1)) * 10);

            eatTimer += deltaTime * eatSpeed;

            Vector2 mouthPos          = SimplePhysicsEnabled ? character.SimPosition : GetMouthPosition().Value;
            Vector2 attackSimPosition = character.Submarine == null?ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;

            Vector2 limbDiff = attackSimPosition - mouthPos;
            float   extent   = Math.Max(mouthLimb.body.GetMaxExtent(), 1);

            if (limbDiff.LengthSquared() < extent * extent)
            {
                //pull the target character to the position of the mouth
                //(+ make the force fluctuate to waggle the character a bit)
                float dragForce = MathHelper.Clamp(eatSpeed * 10, 0, 40);
                if (dragForce > 0.1f)
                {
                    target.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + dragForce));
                    target.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation, dragForce * 2);
                    target.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + dragForce));
                }

                if (InWater)
                {
                    //pull the character's mouth to the target character (again with a fluctuating force)
                    float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
                    mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                }
                else
                {
                    float force = (float)Math.Sin(eatTimer * 100) * mouthLimb.Mass;
                    mouthLimb.body.ApplyLinearImpulse(Vector2.UnitY * force * 2, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                    mouthLimb.body.ApplyTorque(-force * 50);
                }
                var jaw = GetLimb(LimbType.Jaw);
                if (jaw != null)
                {
                    jaw.body.ApplyTorque(-(float)Math.Sin(eatTimer * 150) * jaw.Mass * 25);
                }

                character.ApplyStatusEffects(ActionType.OnEating, deltaTime);

                float particleFrequency = MathHelper.Clamp(eatSpeed / 2, 0.02f, 0.5f);
                if (Rand.Value() < particleFrequency / 6)
                {
                    target.AnimController.MainLimb.AddDamage(target.SimPosition, dmg, 0, 0, false);
                }
                if (Rand.Value() < particleFrequency)
                {
                    target.AnimController.MainLimb.AddDamage(target.SimPosition, 0, dmg, 0, false);
                }
                if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
                {
                    bool CanBeSevered(LimbJoint j) => !j.IsSevered && j.CanBeSevered && j.LimbA != null && !j.LimbA.IsSevered && j.LimbB != null && !j.LimbB.IsSevered;

                    //keep severing joints until there is only one limb left
                    var nonSeveredJoints = target.AnimController.LimbJoints.Where(CanBeSevered);
                    if (nonSeveredJoints.None())
                    {
                        //only one limb left, the character is now full eaten
                        Entity.Spawner?.AddToRemoveQueue(target);
                        character.SelectedCharacter = null;
                    }
                    else //sever a random joint
                    {
                        target.AnimController.SeverLimbJoint(nonSeveredJoints.GetRandom());
                    }
                }
            }
            else
            {
                character.SelectedCharacter = null;
            }
        }
Beispiel #13
0
 public void SetCoolDown(bool applyRandom)
 {
     if (applyRandom)
     {
         float randomFraction = CoolDown * CoolDownRandomFactor;
         CurrentRandomCoolDown  = MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value());
         CoolDownTimer          = CoolDown + CurrentRandomCoolDown;
         randomFraction         = SecondaryCoolDown * CoolDownRandomFactor;
         SecondaryCoolDownTimer = SecondaryCoolDown + MathHelper.Lerp(-randomFraction, randomFraction, Rand.Value());
     }
     else
     {
         CoolDownTimer          = CoolDown;
         SecondaryCoolDownTimer = SecondaryCoolDown;
         CurrentRandomCoolDown  = 0;
     }
 }
Beispiel #14
0
        private void FindSpawnPosition(bool affectSubImmediately)
        {
            if (disallowed)
            {
                return;
            }

            if (Rand.Value(Rand.RandSync.Server) > prefab.SpawnProbability)
            {
                spawnPos = null;
                Finished();
                return;
            }

            spawnPos = Vector2.Zero;
            var  availablePositions = GetAvailableSpawnPositions();
            var  chosenPosition     = new Level.InterestingPosition(Point.Zero, Level.PositionType.MainPath, isValid: false);
            bool isSubOrWreck       = spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Wreck;

            if (affectSubImmediately && !isSubOrWreck && spawnPosType != Level.PositionType.Abyss)
            {
                if (availablePositions.None())
                {
                    //no suitable position found, disable the event
                    spawnPos = null;
                    Finished();
                    return;
                }
                Submarine refSub = GetReferenceSub();
                if (Submarine.MainSubs.Length == 2 && Submarine.MainSubs[1] != null)
                {
                    refSub = Submarine.MainSubs.GetRandom(Rand.RandSync.Unsynced);
                }
                float closestDist = float.PositiveInfinity;
                //find the closest spawnposition that isn't too close to any of the subs
                foreach (var position in availablePositions)
                {
                    Vector2 pos  = position.Position.ToVector2();
                    float   dist = Vector2.DistanceSquared(pos, refSub.WorldPosition);
                    foreach (Submarine sub in Submarine.Loaded)
                    {
                        if (sub.Info.Type != SubmarineType.Player && sub != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle)
                        {
                            continue;
                        }

                        float minDistToSub = GetMinDistanceToSub(sub);
                        if (dist < minDistToSub * minDistToSub)
                        {
                            continue;
                        }

                        if (closestDist == float.PositiveInfinity)
                        {
                            closestDist    = dist;
                            chosenPosition = position;
                            continue;
                        }

                        //chosen position behind the sub -> override with anything that's closer or to the right
                        if (chosenPosition.Position.X < refSub.WorldPosition.X)
                        {
                            if (dist < closestDist || pos.X > refSub.WorldPosition.X)
                            {
                                closestDist    = dist;
                                chosenPosition = position;
                            }
                        }
                        //chosen position ahead of the sub -> only override with a position that's also ahead
                        else if (chosenPosition.Position.X > refSub.WorldPosition.X)
                        {
                            if (dist < closestDist && pos.X > refSub.WorldPosition.X)
                            {
                                closestDist    = dist;
                                chosenPosition = position;
                            }
                        }
                    }
                }
                //only found a spawnpos that's very far from the sub, pick one that's closer
                //and wait for the sub to move further before spawning
                if (closestDist > 15000.0f * 15000.0f)
                {
                    foreach (var position in availablePositions)
                    {
                        float dist = Vector2.DistanceSquared(position.Position.ToVector2(), refSub.WorldPosition);
                        if (dist < closestDist)
                        {
                            closestDist    = dist;
                            chosenPosition = position;
                        }
                    }
                }
            }
            else
            {
                if (!isSubOrWreck)
                {
                    float minDistance = 20000;
                    var   refSub      = GetReferenceSub();
                    availablePositions.RemoveAll(p => Vector2.DistanceSquared(refSub.WorldPosition, p.Position.ToVector2()) < minDistance * minDistance);
                    if (Submarine.MainSubs.Length > 1)
                    {
                        for (int i = 1; i < Submarine.MainSubs.Length; i++)
                        {
                            if (Submarine.MainSubs[i] == null)
                            {
                                continue;
                            }
                            availablePositions.RemoveAll(p => Vector2.DistanceSquared(Submarine.MainSubs[i].WorldPosition, p.Position.ToVector2()) < minDistance * minDistance);
                        }
                    }
                }
                if (availablePositions.None())
                {
                    //no suitable position found, disable the event
                    spawnPos = null;
                    Finished();
                    return;
                }
                chosenPosition = availablePositions.GetRandom();
            }
            if (chosenPosition.IsValid)
            {
                spawnPos = chosenPosition.Position.ToVector2();
                if (chosenPosition.Submarine != null || chosenPosition.Ruin != null)
                {
                    var spawnPoint = WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine, ruin: chosenPosition.Ruin, useSyncedRand: false);
                    if (spawnPoint != null)
                    {
                        System.Diagnostics.Debug.Assert(spawnPoint.Submarine == chosenPosition.Submarine);
                        System.Diagnostics.Debug.Assert(spawnPoint.ParentRuin == chosenPosition.Ruin);
                        spawnPos = spawnPoint.WorldPosition;
                    }
                    else
                    {
                        //no suitable position found, disable the event
                        spawnPos = null;
                        Finished();
                        return;
                    }
                }
                else if ((chosenPosition.PositionType == Level.PositionType.MainPath || chosenPosition.PositionType == Level.PositionType.SidePath) &&
                         offset > 0)
                {
                    Vector2 dir;
                    var     waypoints       = WayPoint.WayPointList.FindAll(wp => wp.Submarine == null);
                    var     nearestWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, spawnPos.Value)).FirstOrDefault();
                    if (nearestWaypoint != null)
                    {
                        int currentIndex = waypoints.IndexOf(nearestWaypoint);
                        var nextWaypoint = waypoints[Math.Min(currentIndex + 20, waypoints.Count - 1)];
                        dir = Vector2.Normalize(nextWaypoint.WorldPosition - nearestWaypoint.WorldPosition);
                        // Ensure that the spawn position is not offset to the left.
                        if (dir.X < 0)
                        {
                            dir.X = 0;
                        }
                    }
                    else
                    {
                        dir = new Vector2(1, Rand.Range(-1, 1));
                    }
                    Vector2 targetPos      = spawnPos.Value + dir * offset;
                    var     targetWaypoint = waypoints.OrderBy(wp => Vector2.DistanceSquared(wp.WorldPosition, targetPos)).FirstOrDefault();
                    if (targetWaypoint != null)
                    {
                        spawnPos = targetWaypoint.WorldPosition;
                    }
                }
                spawnPending = true;
            }
        }
Beispiel #15
0
        private void FindSpawnPosition(bool affectSubImmediately)
        {
            if (disallowed)
            {
                return;
            }

            spawnPos = Vector2.Zero;
            var availablePositions = GetAvailableSpawnPositions();
            var chosenPosition     = new Level.InterestingPosition(Point.Zero, Level.PositionType.MainPath, isValid: false);
            var removedPositions   = new List <Level.InterestingPosition>();

            foreach (var position in availablePositions)
            {
                if (Rand.Value(Rand.RandSync.Server) > prefab.SpawnProbability)
                {
                    removedPositions.Add(position);
                    if (prefab.AllowOnlyOnce)
                    {
                        Level.Loaded.UsedPositions.Add(position);
                    }
                }
            }
            removedPositions.ForEach(p => availablePositions.Remove(p));
            bool isSubOrWreck = spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Wreck;

            if (affectSubImmediately && !isSubOrWreck)
            {
                if (availablePositions.None())
                {
                    //no suitable position found, disable the event
                    Finished();
                    return;
                }
                float closestDist = float.PositiveInfinity;
                //find the closest spawnposition that isn't too close to any of the subs
                foreach (var position in availablePositions)
                {
                    Vector2 pos  = position.Position.ToVector2();
                    float   dist = Vector2.DistanceSquared(pos, Submarine.MainSub.WorldPosition);
                    foreach (Submarine sub in Submarine.Loaded)
                    {
                        if (sub.Info.Type != SubmarineInfo.SubmarineType.Player)
                        {
                            continue;
                        }
                        float minDistToSub = GetMinDistanceToSub(sub);
                        if (dist > minDistToSub * minDistToSub && dist < closestDist)
                        {
                            closestDist    = dist;
                            chosenPosition = position;
                        }
                    }
                }
                //only found a spawnpos that's very far from the sub, pick one that's closer
                //and wait for the sub to move further before spawning
                if (closestDist > 15000.0f * 15000.0f)
                {
                    foreach (var position in availablePositions)
                    {
                        float dist = Vector2.DistanceSquared(position.Position.ToVector2(), Submarine.MainSub.WorldPosition);
                        if (dist < closestDist)
                        {
                            closestDist    = dist;
                            chosenPosition = position;
                        }
                    }
                }
            }
            else
            {
                if (!isSubOrWreck)
                {
                    float minDistance = 20000;
                    availablePositions.RemoveAll(p => Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, p.Position.ToVector2()) < minDistance * minDistance);
                }
                if (availablePositions.None())
                {
                    //no suitable position found, disable the event
                    Finished();
                    return;
                }
                chosenPosition = availablePositions.GetRandom();
            }
            if (chosenPosition.IsValid)
            {
                spawnPos = chosenPosition.Position.ToVector2();
                if (chosenPosition.Submarine != null || chosenPosition.Ruin != null)
                {
                    var spawnPoint = WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine, ruin: chosenPosition.Ruin, useSyncedRand: false);
                    if (spawnPoint != null)
                    {
                        System.Diagnostics.Debug.Assert(spawnPoint.Submarine == chosenPosition.Submarine);
                        System.Diagnostics.Debug.Assert(spawnPoint.ParentRuin == chosenPosition.Ruin);
                        spawnPos = spawnPoint.WorldPosition;
                    }
                }
                spawnPending = true;
                if (prefab.AllowOnlyOnce)
                {
                    Level.Loaded.UsedPositions.Add(chosenPosition);
                }
            }
        }