Example #1
0
        protected override void Act(float deltaTime)
        {
            if (Timing.TotalTime > lastGapUpdate + UpdateGapListInterval || objectiveList == null)
            {
                UpdateGapList();
                lastGapUpdate = Timing.TotalTime;
            }

            if (objectiveList.Any())
            {
                if (!objectiveList[objectiveList.Count - 1].Leak.IsRoomToRoom)
                {
                    if (findDivingGear == null)
                    {
                        findDivingGear = new AIObjectiveFindDivingGear(character, true);
                    }

                    if (!findDivingGear.IsCompleted() && findDivingGear.CanBeCompleted)
                    {
                        findDivingGear.TryComplete(deltaTime);
                        return;
                    }
                }

                objectiveList[objectiveList.Count - 1].TryComplete(deltaTime);

                if (!objectiveList[objectiveList.Count - 1].CanBeCompleted ||
                    objectiveList[objectiveList.Count - 1].IsCompleted())
                {
                    objectiveList.RemoveAt(objectiveList.Count - 1);
                }
            }
            else
            {
                if (idleObjective == null)
                {
                    idleObjective = new AIObjectiveIdle(character);
                }
                idleObjective.TryComplete(deltaTime);
            }
        }
Example #2
0
        protected override void Act(float deltaTime)
        {
            var  currentHull     = character.AnimController.CurrentHull;
            bool needsDivingGear = HumanAIController.NeedsDivingGear(currentHull);
            bool needsDivingSuit = needsDivingGear && (currentHull == null || currentHull.WaterPercentage > 90);
            bool needsEquipment  = false;

            if (needsDivingSuit)
            {
                needsEquipment = !HumanAIController.HasDivingSuit(character);
            }
            else if (needsDivingGear)
            {
                needsEquipment = !HumanAIController.HasDivingMask(character);
            }
            if (needsEquipment)
            {
                TryAddSubObjective(ref divingGearObjective,
                                   () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager),
                                   onAbandon: () => searchHullTimer = Math.Min(1, searchHullTimer));
            }
            else
            {
                if (divingGearObjective != null && divingGearObjective.IsCompleted())
                {
                    // Reset the devotion.
                    Priority            = 0;
                    divingGearObjective = null;
                }
                if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
                {
                    searchHullTimer = Math.Min(1, searchHullTimer);
                }
                if (searchHullTimer > 0.0f)
                {
                    searchHullTimer -= deltaTime;
                }
                else
                {
                    searchHullTimer  = SearchHullInterval;
                    previousSafeHull = currentSafeHull;
                    currentSafeHull  = FindBestHull();
                    if (currentSafeHull == null)
                    {
                        currentSafeHull = previousSafeHull;
                    }
                    if (currentSafeHull != null && currentSafeHull != currentHull)
                    {
                        if (goToObjective?.Target != currentSafeHull)
                        {
                            goToObjective = null;
                        }
                        TryAddSubObjective(ref goToObjective,
                                           constructor: () => new AIObjectiveGoTo(currentSafeHull, character, objectiveManager, getDivingGearIfNeeded: true)
                        {
                            AllowGoingOutside = HumanAIController.HasDivingSuit(character)
                        },
                                           onAbandon: () => unreachable.Add(goToObjective.Target as Hull));
                    }
                    else
                    {
                        goToObjective = null;
                    }
                }
                if (goToObjective != null)
                {
                    if (goToObjective.IsCompleted())
                    {
                        objectiveManager.GetObjective <AIObjectiveIdle>()?.Wander(deltaTime);
                    }
                    Priority = 0;
                    return;
                }
                if (currentHull == null)
                {
                    return;
                }
                //goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found)
                // -> attempt to manually steer away from hazards
                Vector2 escapeVel = Vector2.Zero;
                // TODO: optimize
                foreach (FireSource fireSource in HumanAIController.VisibleHulls.SelectMany(h => h.FireSources))
                {
                    Vector2 dir            = character.Position - fireSource.Position;
                    float   distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
                    escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
                }
                foreach (Character enemy in Character.CharacterList)
                {
                    if (enemy.IsDead || enemy.IsUnconscious || enemy.Removed || HumanAIController.IsFriendly(enemy))
                    {
                        continue;
                    }
                    if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull))
                    {
                        Vector2 dir            = character.Position - enemy.Position;
                        float   distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f);
                        escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
                    }
                }
                if (escapeVel != Vector2.Zero)
                {
                    float left  = currentHull.Rect.X + 50;
                    float right = currentHull.Rect.Right - 50;
                    //only move if we haven't reached the edge of the room
                    if (escapeVel.X < 0 && character.Position.X > left || escapeVel.X > 0 && character.Position.X < right)
                    {
                        character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel);
                    }
                    else
                    {
                        character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left;
                        character.AIController.SteeringManager.Reset();
                    }
                }
                else
                {
                    Priority = 0;
                    objectiveManager.GetObjective <AIObjectiveIdle>()?.Wander(deltaTime);
                }
            }
        }
Example #3
0
        protected override void Act(float deltaTime)
        {
            var currentHull = character.AnimController.CurrentHull;

            if (HumanAIController.NeedsDivingGear(currentHull) && divingGearObjective == null)
            {
                bool needsDivingSuit = currentHull == null || currentHull.WaterPercentage > 90;
                bool hasEquipment    = needsDivingSuit ? HumanAIController.HasDivingSuit(character) : HumanAIController.HasDivingGear(character);
                if (!hasEquipment)
                {
                    divingGearObjective = new AIObjectiveFindDivingGear(character, needsDivingSuit);
                }
            }
            if (divingGearObjective != null)
            {
                divingGearObjective.TryComplete(deltaTime);
                if (divingGearObjective.IsCompleted())
                {
                    divingGearObjective = null;
                    Priority            = 0;
                }
                else if (divingGearObjective.CanBeCompleted)
                {
                    // If diving gear objective is active and can be completed, wait for it to complete.
                    return;
                }
                else
                {
                    divingGearObjective = null;
                    // Reset the timer so that we get a safe hull target.
                    searchHullTimer = 0;
                }
            }

            if (unreachableClearTimer > 0)
            {
                unreachableClearTimer -= deltaTime;
            }
            else
            {
                unreachableClearTimer = clearUnreachableInterval;
                unreachable.Clear();
            }

            if (searchHullTimer > 0.0f)
            {
                searchHullTimer -= deltaTime;
            }
            else if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
            {
                var bestHull = FindBestHull();
                if (bestHull != null && bestHull != currentHull)
                {
                    if (goToObjective != null)
                    {
                        if (goToObjective.Target != bestHull)
                        {
                            // If we need diving gear, we should already have it, if possible.
                            goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false)
                            {
                                AllowGoingOutside = HumanAIController.HasDivingSuit(character)
                            };
                        }
                    }
                    else
                    {
                        goToObjective = new AIObjectiveGoTo(bestHull, character, getDivingGearIfNeeded: false)
                        {
                            AllowGoingOutside = HumanAIController.HasDivingSuit(character)
                        };
                    }
                }
                searchHullTimer = SearchHullInterval;
            }

            if (goToObjective != null)
            {
                goToObjective.TryComplete(deltaTime);
                if (!goToObjective.CanBeCompleted)
                {
                    if (!unreachable.Contains(goToObjective.Target))
                    {
                        unreachable.Add(goToObjective.Target as Hull);
                    }
                    goToObjective = null;
                    HumanAIController.ObjectiveManager.GetObjective <AIObjectiveIdle>().Wander(deltaTime);
                    //SteeringManager.SteeringWander();
                }
            }
            else if (currentHull != null)
            {
                //goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found)
                // -> attempt to manually steer away from hazards
                Vector2 escapeVel = Vector2.Zero;
                foreach (FireSource fireSource in currentHull.FireSources)
                {
                    Vector2 dir            = character.Position - fireSource.Position;
                    float   distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
                    escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
                }

                foreach (Character enemy in Character.CharacterList)
                {
                    //don't run from friendly NPCs
                    if (enemy.TeamID == Character.TeamType.FriendlyNPC)
                    {
                        continue;
                    }
                    //friendly NPCs don't run away from anything but characters controlled by EnemyAIController (= monsters)
                    if (character.TeamID == Character.TeamType.FriendlyNPC && !(enemy.AIController is EnemyAIController))
                    {
                        continue;
                    }

                    if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious &&
                        (enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID))
                    {
                        Vector2 dir            = character.Position - enemy.Position;
                        float   distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f);
                        escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
                    }
                }

                if (escapeVel != Vector2.Zero)
                {
                    //only move if we haven't reached the edge of the room
                    if ((escapeVel.X < 0 && character.Position.X > currentHull.Rect.X + 50) ||
                        (escapeVel.X > 0 && character.Position.X < currentHull.Rect.Right - 50))
                    {
                        character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel);
                    }
                    else
                    {
                        character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left;
                        character.AIController.SteeringManager.Reset();
                    }
                }
                else
                {
                    character.AIController.SteeringManager.Reset();
                }
            }
        }
        protected override void Act(float deltaTime)
        {
            if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead)
            {
                Abandon = true;
                return;
            }
            var otherRescuer = targetCharacter.SelectedBy;

            if (otherRescuer != null && otherRescuer != character)
            {
                // Someone else is rescuing/holding the target.
                Abandon = otherRescuer.IsPlayer || character.GetSkillLevel("medical") < otherRescuer.GetSkillLevel("medical");
                return;
            }
            if (targetCharacter != character)
            {
                if (targetCharacter.IsIncapacitated)
                {
                    // Check if the character needs more oxygen
                    if (!ignoreOxygen && character.SelectedCharacter == targetCharacter || character.CanInteractWith(targetCharacter))
                    {
                        // Replace empty oxygen tank
                        // First remove empty tanks
                        if (HumanAIController.HasItem(targetCharacter, AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR, out IEnumerable <Item> suits, requireEquipped: true))
                        {
                            Item suit = suits.FirstOrDefault();
                            if (suit != null)
                            {
                                AIObjectiveFindDivingGear.EjectEmptyTanks(character, suit, out _);
                            }
                        }
                        else if (HumanAIController.HasItem(targetCharacter, AIObjectiveFindDivingGear.LIGHT_DIVING_GEAR, out IEnumerable <Item> masks, requireEquipped: true))
                        {
                            Item mask = masks.FirstOrDefault();
                            if (mask != null)
                            {
                                AIObjectiveFindDivingGear.EjectEmptyTanks(character, mask, out _);
                            }
                        }
                        bool ShouldRemoveDivingSuit() => targetCharacter.OxygenAvailable < CharacterHealth.InsufficientOxygenThreshold && targetCharacter.CurrentHull?.LethalPressure <= 0;

                        if (ShouldRemoveDivingSuit())
                        {
                            suits.ForEach(suit => suit.Drop(character));
                        }
                        else if (suits.Any() && suits.None(s => s.OwnInventory?.AllItems != null && s.OwnInventory.AllItems.Any(it => it.HasTag(AIObjectiveFindDivingGear.OXYGEN_SOURCE) && it.ConditionPercentage > 0)))
                        {
                            // The target has a suit equipped with an empty oxygen tank.
                            // Can't remove the suit, because the target needs it.
                            // If we happen to have an extra oxygen tank in the inventory, let's swap it.
                            Item spareOxygenTank = FindOxygenTank(targetCharacter) ?? FindOxygenTank(character);
                            if (spareOxygenTank != null)
                            {
                                Item suit = suits.FirstOrDefault();
                                if (suit != null)
                                {
                                    // Insert the new oxygen tank
                                    TryAddSubObjective(ref replaceOxygenObjective, () => new AIObjectiveContainItem(character, spareOxygenTank, suit.GetComponent <ItemContainer>(), objectiveManager),
                                                       onCompleted: () => RemoveSubObjective(ref replaceOxygenObjective),
                                                       onAbandon: () =>
                                    {
                                        RemoveSubObjective(ref replaceOxygenObjective);
                                        ignoreOxygen = true;
                                        if (ShouldRemoveDivingSuit())
                                        {
                                            suits.ForEach(suit => suit.Drop(character));
                                        }
                                    });
                                    return;
                                }
                            }

                            Item FindOxygenTank(Character c) =>
                            c.Inventory.FindItem(i =>
                                                 i.HasTag(AIObjectiveFindDivingGear.OXYGEN_SOURCE) &&
                                                 i.ConditionPercentage > 1 &&
                                                 i.FindParentInventory(inv => inv.Owner is Item otherItem && otherItem.HasTag("diving")) == null,
                                                 recursive: true);
                        }
                    }
                    if (HumanAIController.GetHullSafety(targetCharacter.CurrentHull, targetCharacter) < HumanAIController.HULL_SAFETY_THRESHOLD)
                    {
                        // Incapacitated target is not in a safe place -> Move to a safe place first
                        if (character.SelectedCharacter != targetCharacter)
                        {
                            if (targetCharacter.CurrentHull != null && HumanAIController.VisibleHulls.Contains(targetCharacter.CurrentHull) && targetCharacter.CurrentHull.DisplayName != null)
                            {
                                character.Speak(TextManager.GetWithVariables("DialogFoundUnconsciousTarget", new string[2] {
                                    "[targetname]", "[roomname]"
                                },
                                                                             new string[2] {
                                    targetCharacter.Name, targetCharacter.CurrentHull.DisplayName
                                }, new bool[2] {
                                    false, true
                                }),
                                                null, 1.0f, "foundunconscioustarget" + targetCharacter.Name, 60.0f);
                            }
                            // Go to the target and select it
                            if (!character.CanInteractWith(targetCharacter))
                            {
                                RemoveSubObjective(ref replaceOxygenObjective);
                                RemoveSubObjective(ref goToObjective);
                                TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager)
                                {
                                    CloseEnough        = CloseEnoughToTreat,
                                    DialogueIdentifier = "dialogcannotreachpatient",
                                    TargetName         = targetCharacter.DisplayName
                                },
                                                   onCompleted: () => RemoveSubObjective(ref goToObjective),
                                                   onAbandon: () =>
                                {
                                    RemoveSubObjective(ref goToObjective);
                                    Abandon = true;
                                });
                            }
                            else
                            {
                                character.SelectCharacter(targetCharacter);
                            }
                        }
                        else
                        {
                            // Drag the character into safety
                            if (safeHull == null)
                            {
                                if (findHullTimer > 0)
                                {
                                    findHullTimer -= deltaTime;
                                }
                                else
                                {
                                    safeHull      = objectiveManager.GetObjective <AIObjectiveFindSafety>().FindBestHull(HumanAIController.VisibleHulls);
                                    findHullTimer = findHullInterval * Rand.Range(0.9f, 1.1f);
                                }
                            }
                            if (safeHull != null && character.CurrentHull != safeHull)
                            {
                                RemoveSubObjective(ref replaceOxygenObjective);
                                RemoveSubObjective(ref goToObjective);
                                TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(safeHull, character, objectiveManager),
                                                   onCompleted: () => RemoveSubObjective(ref goToObjective),
                                                   onAbandon: () =>
                                {
                                    RemoveSubObjective(ref goToObjective);
                                    safeHull = character.CurrentHull;
                                });
                            }
                        }
                    }
                }
            }

            if (subObjectives.Any())
            {
                return;
            }

            if (targetCharacter != character && !character.CanInteractWith(targetCharacter))
            {
                RemoveSubObjective(ref replaceOxygenObjective);
                RemoveSubObjective(ref goToObjective);
                // Go to the target and select it
                TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager)
                {
                    CloseEnough        = CloseEnoughToTreat,
                    DialogueIdentifier = "dialogcannotreachpatient",
                    TargetName         = targetCharacter.DisplayName
                },
                                   onCompleted: () => RemoveSubObjective(ref goToObjective),
                                   onAbandon: () =>
                {
                    RemoveSubObjective(ref goToObjective);
                    Abandon = true;
                });
            }
            else
            {
                // We can start applying treatment
                if (character != targetCharacter && character.SelectedCharacter != targetCharacter)
                {
                    if (targetCharacter.CurrentHull.DisplayName != null)
                    {
                        character.Speak(TextManager.GetWithVariables("DialogFoundWoundedTarget", new string[2] {
                            "[targetname]", "[roomname]"
                        },
                                                                     new string[2] {
                            targetCharacter.Name, targetCharacter.CurrentHull.DisplayName
                        }, new bool[2] {
                            false, true
                        }),
                                        null, 1.0f, "foundwoundedtarget" + targetCharacter.Name, 60.0f);
                    }

                    character.SelectCharacter(targetCharacter);
                }
                GiveTreatment(deltaTime);
            }
        }
Example #5
0
        protected override void Act(float deltaTime)
        {
            if (FollowControlledCharacter)
            {
                if (Character.Controlled == null)
                {
                    return;
                }
                Target = Character.Controlled;
            }

            if (Target == character)
            {
                character.AIController.SteeringManager.Reset();
                return;
            }

            waitUntilPathUnreachable -= deltaTime;

            if (!character.IsClimbing)
            {
                character.SelectedConstruction = null;
            }

            if (Target != null)
            {
                character.AIController.SelectTarget(Target.AiTarget);
            }

            Vector2 currTargetPos = Vector2.Zero;

            if (Target == null)
            {
                currTargetPos = targetPos;
            }
            else
            {
                currTargetPos = Target.SimPosition;

                //if character is inside the sub and target isn't, transform the position
                if (character.Submarine != null && Target.Submarine == null)
                {
                    currTargetPos -= character.Submarine.SimPosition;
                }
            }

            if (Vector2.DistanceSquared(currTargetPos, character.SimPosition) < CloseEnough * CloseEnough)
            {
                character.AIController.SteeringManager.Reset();
                character.AnimController.TargetDir = currTargetPos.X > character.SimPosition.X ? Direction.Right : Direction.Left;
            }
            else
            {
                bool targetIsOutside = (Target != null && Target.Submarine == null) ||
                                       (SteeringManager == PathSteering && PathSteering.CurrentPath != null && PathSteering.CurrentPath.HasOutdoorsNodes);
                if (targetIsOutside && character.CurrentHull != null && !AllowGoingOutside)
                {
                    cannotReach = true;
                }
                else
                {
                    character.AIController.SteeringManager.SteeringSeek(currTargetPos);
                    if (getDivingGearIfNeeded)
                    {
                        if (targetIsOutside ||
                            Target is Hull h && HumanAIController.NeedsDivingGear(h) ||
                            Target is Item i && HumanAIController.NeedsDivingGear(i.CurrentHull) ||
                            Target is Character c && HumanAIController.NeedsDivingGear(c.CurrentHull))
                        {
                            if (findDivingGear == null)
                            {
                                findDivingGear = new AIObjectiveFindDivingGear(character, true);
                                AddSubObjective(findDivingGear);
                            }
                            else if (!findDivingGear.CanBeCompleted)
                            {
                                abandon = true;
                            }
                        }
                    }
                }
            }
        }
Example #6
0
        protected override void Act(float deltaTime)
        {
            if (!leak.IsRoomToRoom)
            {
                if (findDivingGear == null)
                {
                    findDivingGear = new AIObjectiveFindDivingGear(character, true);
                    AddSubObjective(findDivingGear);
                }
                else if (!findDivingGear.CanBeCompleted)
                {
                    abandon = true;
                    return;
                }
            }

            var weldingTool = character.Inventory.FindItemByTag("weldingtool");

            if (weldingTool == null)
            {
                AddSubObjective(new AIObjectiveGetItem(character, "weldingtool", true));
                return;
            }
            else
            {
                var containedItems = weldingTool.ContainedItems;
                if (containedItems == null)
                {
                    return;
                }

                var fuelTank = containedItems.FirstOrDefault(i => i.HasTag("weldingfueltank") && i.Condition > 0.0f);
                if (fuelTank == null)
                {
                    AddSubObjective(new AIObjectiveContainItem(character, "weldingfueltank", weldingTool.GetComponent <ItemContainer>()));
                    return;
                }
            }

            var repairTool = weldingTool.GetComponent <RepairTool>();

            if (repairTool == null)
            {
                return;
            }

            Vector2 gapDiff = leak.WorldPosition - character.WorldPosition;

            // TODO: use the collider size/reach?
            if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
            {
                HumanAIController.AnimController.Crouching = true;
            }

            //float reach = HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range);
            float reach       = ConvertUnits.ToSimUnits(repairTool.Range);
            bool  cannotReach = ConvertUnits.ToSimUnits(gapDiff.Length()) > reach;

            if (cannotReach)
            {
                if (gotoObjective != null)
                {
                    // Check if the objective is already removed -> completed/impossible
                    if (!subObjectives.Contains(gotoObjective))
                    {
                        if (!gotoObjective.CanBeCompleted)
                        {
                            abandon = true;
                        }
                        gotoObjective = null;
                        return;
                    }
                }
                else
                {
                    gotoObjective = new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character)
                    {
                        CloseEnough = reach
                    };
                    if (!subObjectives.Contains(gotoObjective))
                    {
                        AddSubObjective(gotoObjective);
                    }
                }
            }
            if (gotoObjective == null || gotoObjective.IsCompleted())
            {
                if (operateObjective == null)
                {
                    operateObjective = new AIObjectiveOperateItem(repairTool, character, "", true, leak);
                    AddSubObjective(operateObjective);
                }
                else if (!subObjectives.Contains(operateObjective))
                {
                    operateObjective = null;
                }
            }
        }