Esempio n. 1
0
        protected override void Act(float deltaTime)
        {
            // Only continue when the get item sub objectives have been completed.
            if (subObjectives.Any())
            {
                return;
            }
            foreach (Repairable repairable in Item.Repairables)
            {
                if (!repairable.HasRequiredItems(character, false))
                {
                    //make sure we have all the items required to fix the target item
                    foreach (var kvp in repairable.requiredItems)
                    {
                        foreach (RelatedItem requiredItem in kvp.Value)
                        {
                            var getItemObjective = new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true);
                            if (objectiveManager.IsCurrentOrder <AIObjectiveRepairItems>())
                            {
                                if (character.IsOnPlayerTeam)
                                {
                                    getItemObjective.Abandoned += () => character.Speak(TextManager.Get("dialogcannotfindrequireditemtorepair"), null, 0.0f, "dialogcannotfindrequireditemtorepair", 10.0f);
                                }
                            }
                            subObjectives.Add(getItemObjective);
                        }
                    }
                    return;
                }
            }
            if (repairTool == null)
            {
                FindRepairTool();
            }
            if (repairTool != null)
            {
                if (repairTool.Item.OwnInventory == null)
                {
#if DEBUG
                    DebugConsole.ThrowError($"{character.Name}: AIObjectiveRepairItem failed - the item \"" + repairTool + "\" has no proper inventory");
#endif
                    Abandon = true;
                    return;
                }
                HumanAIController.UnequipContainedItems(repairTool.Item, it => !it.HasTag("weldingfuel"));
                HumanAIController.UnequipEmptyItems(repairTool.Item);
                RelatedItem item = null;
                Item        fuel = null;
                foreach (RelatedItem requiredItem in repairTool.requiredItems[RelatedItem.RelationType.Contained])
                {
                    item = requiredItem;
                    fuel = repairTool.Item.OwnInventory.AllItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
                    if (fuel != null)
                    {
                        break;
                    }
                }
                if (fuel == null)
                {
                    RemoveSubObjective(ref goToObjective);
                    TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, item.Identifiers, repairTool.Item.GetComponent <ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC),
                                       onCompleted: () => RemoveSubObjective(ref refuelObjective),
                                       onAbandon: () => Abandon = true);
                    return;
                }
            }
            if (!character.IsClimbing && character.CanInteractWith(Item, out _, checkLinked: false))
            {
                HumanAIController.FaceTarget(Item);
                if (repairTool != null)
                {
                    OperateRepairTool(deltaTime);
                }
                foreach (Repairable repairable in Item.Repairables)
                {
                    if (repairable.CurrentFixer != null && repairable.CurrentFixer != character)
                    {
                        // Someone else is repairing the target. Abandon the objective if the other is better at this than us.
                        Abandon = repairable.CurrentFixer.IsPlayer || repairable.DegreeOfSuccess(character) < repairable.DegreeOfSuccess(repairable.CurrentFixer);
                    }
                    if (!Abandon)
                    {
                        if (character.SelectedConstruction != Item)
                        {
                            if (!Item.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true) &&
                                !Item.TryInteract(character, ignoreRequiredItems: true, forceActionKey: true))
                            {
                                Abandon = true;
                            }
                        }
                        if (previousCondition == -1)
                        {
                            previousCondition = Item.Condition;
                        }
                        else if (Item.Condition < previousCondition)
                        {
                            // If the current condition is less than the previous condition, we can't complete the task, so let's abandon it. The item is probably deteriorating at a greater speed than we can repair it.
                            Abandon = true;
                        }
                    }
                    if (Abandon)
                    {
                        if (character.IsOnPlayerTeam && IsRepairing())
                        {
                            character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f);
                        }
                        repairable.StopRepairing(character);
                    }
                    else if (repairable.CurrentFixer != character)
                    {
                        repairable.StartRepairing(character, Repairable.FixActions.Repair);
                    }
                    break;
                }
            }
            else
            {
                RemoveSubObjective(ref refuelObjective);
                // If cannot reach the item, approach it.
                TryAddSubObjective(ref goToObjective,
                                   constructor: () =>
                {
                    previousCondition = -1;
                    var objective     = new AIObjectiveGoTo(Item, character, objectiveManager)
                    {
                        // Don't stop in ladders, because we can't interact with other items while holding the ladders.
                        endNodeFilter = node => node.Waypoint.Ladders == null
                    };
                    if (repairTool != null)
                    {
                        objective.CloseEnough = repairTool.Range * 0.75f;
                    }
                    return(objective);
                },
                                   onAbandon: () =>
                {
                    Abandon = true;
                    if (character.IsOnPlayerTeam && IsRepairing())
                    {
                        character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f);
                    }
                });
            }
        }
Esempio n. 2
0
        protected override void Act(float deltaTime)
        {
            if (container == null || (container.Item != null && container.Item.IsThisOrAnyContainerIgnoredByAI()))
            {
                Abandon = true;
                return;
            }
            ItemToContain = item ?? character.Inventory.FindItem(i => CheckItem(i) && i.Container != container.Item, recursive: true);
            if (ItemToContain != null)
            {
                if (!character.CanInteractWith(ItemToContain, checkLinked: false))
                {
                    Abandon = true;
                    return;
                }
                if (character.CanInteractWith(container.Item, checkLinked: false))
                {
                    if (RemoveExisting)
                    {
                        HumanAIController.UnequipContainedItems(container.Item);
                    }
                    else if (RemoveEmpty)
                    {
                        HumanAIController.UnequipEmptyItems(container.Item);
                    }
                    Inventory originalInventory = ItemToContain.ParentInventory;
                    var       slots             = originalInventory?.FindIndices(ItemToContain);
                    if (container.Inventory.TryPutItem(ItemToContain, null))
                    {
                        if (MoveWholeStack && slots != null)
                        {
                            foreach (int slot in slots)
                            {
                                foreach (Item item in originalInventory.GetItemsAt(slot).ToList())
                                {
                                    container.Inventory.TryPutItem(item, null);
                                }
                            }

                            IsCompleted = true;
                        }
                    }
                    else
                    {
                        if (ItemToContain.ParentInventory == character.Inventory && character.Submarine == Submarine.MainSub)
                        {
                            ItemToContain.Drop(character);
                        }
                        Abandon = true;
                    }
                }
                else
                {
                    TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(container.Item, character, objectiveManager, getDivingGearIfNeeded: AllowToFindDivingGear)
                    {
                        DialogueIdentifier = "dialogcannotreachtarget",
                        TargetName         = container.Item.Name,
                        abortCondition     = obj => !ItemToContain.IsOwnedBy(character),
                        SpeakIfFails       = !objectiveManager.IsCurrentOrder <AIObjectiveCleanupItems>()
                    },
                                       onAbandon: () => Abandon = true,
                                       onCompleted: () => RemoveSubObjective(ref goToObjective));
                }
            }
            else
            {
                if (character.Submarine == null)
                {
                    Abandon = true;
                }
                else
                {
                    // No matching items in the inventory, try to get an item
                    TryAddSubObjective(ref getItemObjective, () =>
                                       new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, equip: Equip, checkInventory: checkInventory, spawnItemIfNotFound: spawnItemIfNotFound)
                    {
                        GetItemPriority             = GetItemPriority,
                        ignoredContainerIdentifiers = ignoredContainerIdentifiers,
                        ignoredItems           = containedItems,
                        AllowToFindDivingGear  = AllowToFindDivingGear,
                        AllowDangerousPressure = AllowDangerousPressure,
                        TargetCondition        = ConditionLevel
                    }, onAbandon: () =>
                    {
                        Abandon = true;
                    }, onCompleted: () =>
                    {
                        if (getItemObjective?.TargetItem != null)
                        {
                            containedItems.Add(getItemObjective.TargetItem);
                        }
                        RemoveSubObjective(ref getItemObjective);
                    });
                }
            }
        }
Esempio n. 3
0
        protected override void Act(float deltaTime)
        {
            var weldingTool = character.Inventory.FindItemByTag("weldingequipment", true);

            if (weldingTool == null)
            {
                TryAddSubObjective(ref getWeldingTool, () => new AIObjectiveGetItem(character, "weldingequipment", objectiveManager, equip: true, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC),
                                   onAbandon: () =>
                {
                    if (character.IsOnPlayerTeam && objectiveManager.IsCurrentOrder <AIObjectiveFixLeaks>())
                    {
                        character.Speak(TextManager.Get("dialogcannotfindweldingequipment"), null, 0.0f, "dialogcannotfindweldingequipment", 10.0f);
                    }
                    Abandon = true;
                },
                                   onCompleted: () => RemoveSubObjective(ref getWeldingTool));
                return;
            }
            else
            {
                if (weldingTool.OwnInventory == null)
                {
#if DEBUG
                    DebugConsole.ThrowError($"{character.Name}: AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no proper inventory");
#endif
                    Abandon = true;
                    return;
                }
                HumanAIController.UnequipContainedItems(weldingTool, it => !it.HasTag("weldingfuel"));
                HumanAIController.UnequipEmptyItems(weldingTool);
                if (weldingTool.OwnInventory != null && weldingTool.OwnInventory.AllItems.None(i => i.HasTag("weldingfuel") && i.Condition > 0.0f))
                {
                    TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, "weldingfuel", weldingTool.GetComponent <ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC),
                                       onAbandon: () =>
                    {
                        Abandon = true;
                        ReportWeldingFuelTankCount();
                    },
                                       onCompleted: () =>
                    {
                        RemoveSubObjective(ref refuelObjective);
                        ReportWeldingFuelTankCount();
                    });

                    void ReportWeldingFuelTankCount()
                    {
                        int remainingOxygenTanks = Submarine.MainSub.GetItems(false).Count(i => i.HasTag("weldingfuel") && i.Condition > 1);

                        if (remainingOxygenTanks == 0)
                        {
                            character.Speak(TextManager.Get("DialogOutOfWeldingFuel"), null, 0.0f, "outofweldingfuel", 30.0f);
                        }
                        else if (remainingOxygenTanks < 4)
                        {
                            character.Speak(TextManager.Get("DialogLowOnWeldingFuel"), null, 0.0f, "lowonweldingfuel", 30.0f);
                        }
                    }

                    return;
                }
            }
            if (subObjectives.Any())
            {
                return;
            }
            var repairTool = weldingTool.GetComponent <RepairTool>();
            if (repairTool == null)
            {
#if DEBUG
                DebugConsole.ThrowError($"{character.Name}: AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
#endif
                Abandon = true;
                return;
            }
            Vector2 toLeak = Leak.WorldPosition - character.WorldPosition;
            // TODO: use the collider size/reach?
            if (!character.AnimController.InWater && Math.Abs(toLeak.X) < 100 && toLeak.Y < 0.0f && toLeak.Y > -150)
            {
                HumanAIController.AnimController.Crouching = true;
            }
            float reach      = CalculateReach(repairTool, character);
            bool  canOperate = toLeak.LengthSquared() < reach * reach;
            if (canOperate)
            {
                TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak),
                                   onAbandon: () => Abandon = true,
                                   onCompleted: () =>
                {
                    if (Check())
                    {
                        IsCompleted = true;
                    }
                    else
                    {
                        // Failed to operate. Probably too far.
                        Abandon = true;
                    }
                });
            }
            else
            {
                TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(Leak, character, objectiveManager)
                {
                    CloseEnough        = reach,
                    DialogueIdentifier = Leak.FlowTargetHull != null ? "dialogcannotreachleak" : null,
                    TargetName         = Leak.FlowTargetHull?.DisplayName,
                    CheckVisibility    = false
                },
                                   onAbandon: () =>
                {
                    if (Check())
                    {
                        IsCompleted = true;
                    }
                    else if ((Leak.WorldPosition - character.WorldPosition).LengthSquared() > MathUtils.Pow(reach * 2, 2))
                    {
                        // Too far
                        Abandon = true;
                    }
                    else
                    {
                        // We are close, try again.
                        RemoveSubObjective(ref gotoObjective);
                    }
                },
                                   onCompleted: () => RemoveSubObjective(ref gotoObjective));
            }
        }
Esempio n. 4
0
        protected override void Act(float deltaTime)
        {
            if (container?.Item == null || container.Item.Removed || container.Item.IsThisOrAnyContainerIgnoredByAI(character))
            {
                Abandon = true;
                return;
            }
            ItemToContain = item ?? character.Inventory.FindItem(i => CheckItem(i) && i.Container != container.Item, recursive: true);
            if (ItemToContain != null)
            {
                if (!character.CanInteractWith(ItemToContain, checkLinked: false))
                {
                    Abandon = true;
                    return;
                }
                if (character.CanInteractWith(container.Item, checkLinked: false))
                {
                    if (RemoveExisting)
                    {
                        HumanAIController.UnequipContainedItems(container.Item);
                    }
                    else if (RemoveEmpty)
                    {
                        HumanAIController.UnequipEmptyItems(container.Item);
                    }
                    Inventory originalInventory = ItemToContain.ParentInventory;
                    var       slots             = originalInventory?.FindIndices(ItemToContain);
                    if (container.Inventory.TryPutItem(ItemToContain, null))
                    {
                        if (MoveWholeStack && slots != null)
                        {
                            foreach (int slot in slots)
                            {
                                foreach (Item item in originalInventory.GetItemsAt(slot).ToList())
                                {
                                    container.Inventory.TryPutItem(item, null);
                                }
                            }

                            IsCompleted = true;
                        }
                    }
                    else
                    {
                        if (ItemToContain.ParentInventory == character.Inventory && character.IsInFriendlySub)
                        {
                            ItemToContain.Drop(character);
                        }
                        Abandon = true;
                    }
                }
                else
                {
                    TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(container.Item, character, objectiveManager, getDivingGearIfNeeded: AllowToFindDivingGear)
                    {
                        DialogueIdentifier = "dialogcannotreachtarget",
                        TargetName         = container.Item.Name,
                        AbortCondition     = obj =>
                                             container?.Item == null || container.Item.Removed || container.Item.IsThisOrAnyContainerIgnoredByAI(character) ||
                                             ItemToContain == null || ItemToContain.Removed ||
                                             !ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
                        SpeakIfFails = !objectiveManager.IsCurrentOrder <AIObjectiveCleanupItems>()
                    },
Esempio n. 5
0
        protected override void Act(float deltaTime)
        {
            if (character.LockHands)
            {
                Abandon = true;
                return;
            }
            targetItem = character.Inventory.FindItemByTag(gearTag, true);
            if (targetItem == null || !character.HasEquippedItem(targetItem) && targetItem.ContainedItems.Any(i => i.HasTag(OXYGEN_SOURCE) && i.Condition > 0))
            {
                TryAddSubObjective(ref getDivingGear, () =>
                {
                    if (targetItem == null && character.IsOnPlayerTeam)
                    {
                        character.Speak(TextManager.Get("DialogGetDivingGear"), null, 0.0f, "getdivinggear", 30.0f);
                    }
                    return(new AIObjectiveGetItem(character, gearTag, objectiveManager, equip: true)
                    {
                        AllowStealing = true,
                        AllowToFindDivingGear = false,
                        AllowDangerousPressure = true
                    });
                },
                                   onAbandon: () => Abandon = true,
                                   onCompleted: () => RemoveSubObjective(ref getDivingGear));
            }
            else
            {
                HumanAIController.UnequipContainedItems(targetItem, it => !it.HasTag("oxygensource"));
                HumanAIController.UnequipEmptyItems(targetItem);
                // Seek oxygen that has at least 10% condition left, if we are inside a friendly sub.
                // The margin helps us to survive, because we might need some oxygen before we can find more oxygen.
                // When we are venturing outside of our sub, let's just suppose that we have enough oxygen with us and optimize it so that we don't keep switching off half used tanks.
                float min = character.Submarine != Submarine.MainSub ? 0.01f : MIN_OXYGEN;
                if (targetItem.OwnInventory != null && targetItem.OwnInventory.AllItems.None(it => it != null && it.HasTag(OXYGEN_SOURCE) && it.Condition > min))
                {
                    TryAddSubObjective(ref getOxygen, () =>
                    {
                        if (character.IsOnPlayerTeam)
                        {
                            if (HumanAIController.HasItem(character, "oxygensource", out _, conditionPercentage: min))
                            {
                                character.Speak(TextManager.Get("dialogswappingoxygentank"), null, 0, "swappingoxygentank", 30.0f);
                            }
                            else
                            {
                                character.Speak(TextManager.Get("DialogGetOxygenTank"), null, 0, "getoxygentank", 30.0f);
                            }
                        }
                        return(new AIObjectiveContainItem(character, OXYGEN_SOURCE, targetItem.GetComponent <ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC)
                        {
                            AllowToFindDivingGear = false,
                            AllowDangerousPressure = true,
                            ConditionLevel = MIN_OXYGEN,
                            RemoveExisting = true
                        });
                    },
                                       onAbandon: () =>
                    {
                        getOxygen          = null;
                        int remainingTanks = ReportOxygenTankCount();
                        // Try to seek any oxygen sources, even if they have minimal amount of oxygen.
                        TryAddSubObjective(ref getOxygen, () =>
                        {
                            return(new AIObjectiveContainItem(character, OXYGEN_SOURCE, targetItem.GetComponent <ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC)
                            {
                                AllowToFindDivingGear = false,
                                AllowDangerousPressure = true,
                                RemoveExisting = true
                            });
                        },
                                           onAbandon: () =>
                        {
                            Abandon = true;
                            if (remainingTanks > 0 && !HumanAIController.HasItem(character, "oxygensource", out _, conditionPercentage: 0.01f))
                            {
                                character.Speak(TextManager.Get("dialogcantfindtoxygen"), null, 0, "cantfindoxygen", 30.0f);
                            }
                        },
                                           onCompleted: () => RemoveSubObjective(ref getOxygen));
                    },
                                       onCompleted: () =>
                    {
                        RemoveSubObjective(ref getOxygen);
                        ReportOxygenTankCount();
                    });

                    int ReportOxygenTankCount()
                    {
                        int remainingOxygenTanks = Submarine.MainSub.GetItems(false).Count(i => i.HasTag("oxygensource") && i.Condition > 1);

                        if (remainingOxygenTanks == 0)
                        {
                            character.Speak(TextManager.Get("DialogOutOfOxygenTanks"), null, 0.0f, "outofoxygentanks", 30.0f);
                        }
                        else if (remainingOxygenTanks < 10)
                        {
                            character.Speak(TextManager.Get("DialogLowOnOxygenTanks"), null, 0.0f, "lowonoxygentanks", 30.0f);
                        }
                        return(remainingOxygenTanks);
                    }
                }
            }
        }