Beispiel #1
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            switch (objective.Option.ToLowerInvariant())
            {
            case "power up":
                float tempDiff = load - temperature;

                shutDownTemp = Math.Min(load + 1000.0f, 7500.0f);

                //temperature too high/low
                if (Math.Abs(tempDiff) > 500.0f)
                {
                    AutoTemp     = false;
                    FissionRate += deltaTime * 100.0f * Math.Sign(tempDiff);
                    CoolingRate -= deltaTime * 100.0f * Math.Sign(tempDiff);
                }
                //temperature OK
                else
                {
                    AutoTemp = true;
                }

                break;

            case "shutdown":

                shutDownTemp = 0.0f;

                break;
            }

            return(false);
        }
Beispiel #2
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
#if CLIENT
            if (GameMain.Client != null)
            {
                return(false);
            }
#endif

            if (objective.Option.ToLowerInvariant() == "stoppumping")
            {
#if SERVER
                if (FlowPercentage > 0.0f)
                {
                    item.CreateServerEvent(this);
                }
#endif
                FlowPercentage = 0.0f;
            }
            else
            {
#if SERVER
                if (!IsActive || FlowPercentage > -100.0f)
                {
                    item.CreateServerEvent(this);
                }
#endif
                IsActive       = true;
                FlowPercentage = -100.0f;
            }
            return(true);
        }
Beispiel #3
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
#if CLIENT
            if (GameMain.Client != null)
            {
                return(false);
            }
#endif

            if (objective.Option.Equals("stoppumping", StringComparison.OrdinalIgnoreCase))
            {
#if SERVER
                if (objective.Override || FlowPercentage > 0.0f)
                {
                    item.CreateServerEvent(this);
                }
#endif
                IsActive       = false;
                FlowPercentage = 0.0f;
            }
            else
            {
#if SERVER
                if (objective.Override || !IsActive || FlowPercentage > -100.0f)
                {
                    item.CreateServerEvent(this);
                }
#endif
                IsActive       = true;
                FlowPercentage = -100.0f;
            }
            return(true);
        }
Beispiel #4
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (objective.Override)
            {
                if (user != character && user != null && user.SelectedConstruction == item)
                {
                    character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
                }
            }
            user = character;
            if (!AutoPilot)
            {
                unsentChanges = true;
                AutoPilot     = true;
            }
            switch (objective.Option.ToLowerInvariant())
            {
            case "maintainposition":
                if (objective.Override)
                {
                    if (!MaintainPos)
                    {
                        unsentChanges = true;
                        MaintainPos   = true;
                    }
                    if (!posToMaintain.HasValue)
                    {
                        unsentChanges = true;
                        posToMaintain = controlledSub != null ?
                                        controlledSub.WorldPosition :
                                        item.Submarine == null ? item.WorldPosition : item.Submarine.WorldPosition;
                    }
                }
                break;

            case "navigateback":
                if (objective.Override)
                {
                    if (MaintainPos || LevelEndSelected || !LevelStartSelected)
                    {
                        unsentChanges = true;
                    }
                    SetDestinationLevelStart();
                }
                break;

            case "navigatetodestination":
                if (objective.Override)
                {
                    if (MaintainPos || !LevelEndSelected || LevelStartSelected)
                    {
                        unsentChanges = true;
                    }
                    SetDestinationLevelEnd();
                }
                break;
            }
            sonar?.AIOperate(deltaTime, character, objective);
            return(false);
        }
Beispiel #5
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
#if CLIENT
            if (GameMain.Client != null)
            {
                return(false);
            }
#endif

            if (string.IsNullOrEmpty(objective.Option) || objective.Option.ToLowerInvariant() == "charge")
            {
                if (Math.Abs(rechargeSpeed - maxRechargeSpeed * aiRechargeTargetRatio) > 0.05f)
                {
#if SERVER
                    item.CreateServerEvent(this);
#endif
                    RechargeSpeed = maxRechargeSpeed * aiRechargeTargetRatio;
#if CLIENT
                    rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f);
#endif

                    character.Speak(TextManager.GetWithVariables("DialogChargeBatteries", new string[2] {
                        "[itemname]", "[rate]"
                    },
                                                                 new string[2] {
                        item.Name, ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString()
                    },
                                                                 new bool[2] {
                        true, false
                    }), null, 1.0f, "chargebattery", 10.0f);
                }
            }
            else
            {
                if (rechargeSpeed > 0.0f)
                {
#if SERVER
                    item.CreateServerEvent(this);
#endif
                    RechargeSpeed = 0.0f;
#if CLIENT
                    rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f);
#endif
                    character.Speak(TextManager.GetWithVariables("DialogStopChargingBatteries", new string[2] {
                        "[itemname]", "[rate]"
                    },
                                                                 new string[2] {
                        item.Name, ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString()
                    },
                                                                 new bool[2] {
                        true, false
                    }), null, 1.0f, "chargebattery", 10.0f);
                }
            }

            return(true);
        }
Beispiel #6
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (!IsActive || !aiPingCheckPending)
            {
                return(false);
            }

            Dictionary <string, List <Character> > targetGroups = new Dictionary <string, List <Character> >();

            foreach (Character c in Character.CharacterList)
            {
                if (c.AnimController.CurrentHull != null || !c.Enabled)
                {
                    continue;
                }
                if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null)
                {
                    continue;
                }
                if (Vector2.DistanceSquared(c.WorldPosition, item.WorldPosition) > range * range)
                {
                    continue;
                }

                string directionName = GetDirectionName(c.WorldPosition - item.WorldPosition);

                if (!targetGroups.ContainsKey(directionName))
                {
                    targetGroups.Add(directionName, new List <Character>());
                }
                targetGroups[directionName].Add(c);
            }

            foreach (KeyValuePair <string, List <Character> > targetGroup in targetGroups)
            {
                string dialogTag = "DialogSonarTarget";
                if (targetGroup.Value.Count > 1)
                {
                    dialogTag = "DialogSonarTargetMultiple";
                }
                else if (targetGroup.Value[0].Mass > 100.0f)
                {
                    dialogTag = "DialogSonarTargetLarge";
                }
                character.Speak(TextManager.Get(dialogTag).Replace("[direction]", targetGroup.Key).Replace("[count]", targetGroup.Value.Count.ToString()),
                                null, 0, "sonartarget" + targetGroup.Value[0].ID, 30);

                //prevent the character from reporting other targets in the group
                for (int i = 1; i < targetGroup.Value.Count; i++)
                {
                    character.DisableLine("sonartarget" + targetGroup.Value[i].ID);
                }
            }

            return(true);
        }
Beispiel #7
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            Gap leak = objective.OperateTarget as Gap;

            if (leak == null)
            {
                return(true);
            }

            float dist = Vector2.Distance(leak.WorldPosition, item.WorldPosition);

            //too far away -> consider this done and hope the AI is smart enough to move closer
            if (dist > range * 5.0f)
            {
                return(true);
            }

            //steer closer if almost in range
            if (dist > range)
            {
                Vector2 standPos = leak.IsHorizontal ?
                                   new Vector2(Math.Sign(item.WorldPosition.X - leak.WorldPosition.X), 0.0f)
                    : new Vector2(0.0f, Math.Sign(item.WorldPosition.Y - leak.WorldPosition.Y));

                standPos = leak.WorldPosition + standPos * range;

                character.AIController.SteeringManager.SteeringManual(deltaTime, (standPos - character.WorldPosition) / 1000.0f);
            }
            else
            {
                //close enough -> stop moving
                character.AIController.SteeringManager.Reset();
            }

            character.CursorPosition = leak.Position;
            character.SetInput(InputType.Aim, false, true);

            Use(deltaTime, character);

            return(leak.Open <= 0.0f);
        }
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            Gap leak = objective.OperateTarget as Gap;

            if (leak == null)
            {
                return(true);
            }

            Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
            float   dist           = fromItemToLeak.Length();

            //too far away -> consider this done and hope the AI is smart enough to move closer
            if (dist > Range * 3.0f)
            {
                return(true);
            }

            // TODO: use the collider size?
            if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
                Math.Abs(fromItemToLeak.X) < 100.0f && fromItemToLeak.Y < 0.0f && fromItemToLeak.Y > -150.0f)
            {
                ((HumanoidAnimController)character.AnimController).Crouching = true;
            }

            //steer closer if almost in range
            if (dist > Range)
            {
                Vector2 standPos = new Vector2(Math.Sign(-fromItemToLeak.X), Math.Sign(-fromItemToLeak.Y)) / 2;
                if (!character.AnimController.InWater)
                {
                    if (leak.IsHorizontal)
                    {
                        standPos.X *= 2;
                        standPos.Y  = 0;
                    }
                    else
                    {
                        standPos.X = 0;
                    }
                }
                if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
                {
                    if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
                    {
                        Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
                        character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
                    }
                    else
                    {
                        character.AIController.SteeringManager.SteeringSeek(standPos);
                    }
                }
                else
                {
                    character.AIController.SteeringManager.SteeringSeek(standPos);
                }
            }
            else
            {
                if (dist < Range / 2)
                {
                    // Too close -> steer away
                    character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2);
                }
                else if (dist <= Range)
                {
                    // In range
                    character.AIController.SteeringManager.Reset();
                }
                else
                {
                    return(false);
                }
            }
            sinTime += deltaTime;
            character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist);
            if (item.RequireAimToUse)
            {
                bool isOperatingButtons = false;
                if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
                {
                    var door = indoorSteering.CurrentPath?.CurrentNode?.ConnectedDoor;
                    if (door != null && !door.IsOpen)
                    {
                        isOperatingButtons = door.HasIntegratedButtons || door.Item.GetConnectedComponents <Controller>(true).Any();
                    }
                }
                if (!isOperatingButtons)
                {
                    character.SetInput(InputType.Aim, false, true);
                }
            }
            // Press the trigger only when the tool is approximately facing the target.
            var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);

            if (angle < MathHelper.PiOver4)
            {
                character.SetInput(InputType.Shoot, false, true);
                Use(deltaTime, character);
            }

            bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
                             (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);

            if (leakFixed && leak.FlowTargetHull != null)
            {
                sinTime = 0;
                if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
                {
                    character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f);
                }
                else
                {
                    character.Speak(TextManager.GetWithVariable("DialogLeakFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leakfixed", 10.0f);
                }
            }

            return(leakFixed);
        }
Beispiel #9
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return(false);
            }

            IsActive = true;

            float degreeOfSuccess = DegreeOfSuccess(character);

            //characters with insufficient skill levels don't refuel the reactor
            if (degreeOfSuccess > 0.2f)
            {
                if (objective.SubObjectives.None())
                {
                    var containedItems = item.ContainedItems;
                    foreach (Item fuelRod in containedItems)
                    {
                        if (fuelRod != null && fuelRod.Condition <= 0.0f)
                        {
                            if (!FindSuitableContainer(character,
                                                       i =>
                            {
                                var container = i.GetComponent <ItemContainer>();
                                if (container == null)
                                {
                                    return(0);
                                }
                                if (container.Inventory.IsFull())
                                {
                                    return(0);
                                }
                                if (container.ShouldBeContained(fuelRod, out bool isRestrictionsDefined))
                                {
                                    if (isRestrictionsDefined)
                                    {
                                        return(3);
                                    }
                                    else
                                    {
                                        if (fuelRod.Prefab.IsContainerPreferred(container, out bool isPreferencesDefined))
                                        {
                                            return(isPreferencesDefined ? 2 : 1);
                                        }
                                        else
                                        {
                                            return(isPreferencesDefined ? 0 : 1);
                                        }
                                    }
                                }
                                else
                                {
                                    return(0);
                                }
                            }, out Item targetContainer))
                            {
                                return(false);
                            }
                            var decontainObjective = new AIObjectiveDecontainItem(character, fuelRod, item.GetComponent <ItemContainer>(), objective.objectiveManager, targetContainer?.GetComponent <ItemContainer>());
                            decontainObjective.Abandoned += () =>
                            {
                                itemIndex = 0;
                                if (targetContainer != null)
                                {
                                    ignoredContainers.Add(targetContainer);
                                }
                            };
                            objective.AddSubObjectiveInQueue(decontainObjective);
                        }
                    }
                }

                if (aiUpdateTimer > 0.0f)
                {
                    aiUpdateTimer -= deltaTime;
                    return(false);
                }

                //load more fuel if the current maximum output is only 50% of the current load
                if (NeedMoreFuel(minimumOutputRatio: 0.5f))
                {
                    aiUpdateTimer = AIUpdateInterval;
                    if (objective.SubObjectives.None())
                    {
                        var containFuelObjective = new AIObjectiveContainItem(character, fuelTags, item.GetComponent <ItemContainer>(), objective.objectiveManager)
                        {
                            targetItemCount = item.ContainedItems.Count(i => i != null && fuelTags.Any(t => i.Prefab.Identifier == t || i.HasTag(t))) + 1,
                            GetItemPriority = (Item fuelItem) =>
                            {
                                if (fuelItem.ParentInventory?.Owner is Item)
                                {
                                    //don't take fuel from other reactors
                                    if (((Item)fuelItem.ParentInventory.Owner).GetComponent <Reactor>() != null)
                                    {
                                        return(0.0f);
                                    }
                                }
                                return(1.0f);
                            }
                        };
                        containFuelObjective.Abandoned += () => objective.Abandon = true;
                        objective.AddSubObjective(containFuelObjective);
                        character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
                    }
                    return(false);
                }
                else if (TooMuchFuel())
                {
                    foreach (Item item in item.ContainedItems)
                    {
                        if (item != null && fuelTags.Any(t => item.Prefab.Identifier == t || item.HasTag(t)))
                        {
                            if (!character.Inventory.TryPutItem(item, character, allowedSlots: item.AllowedSlots))
                            {
                                item.Drop(character);
                            }
                            break;
                        }
                    }
                }
            }

            if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item)
            {
                character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
            }

            LastUser = lastAIUser = character;

            bool  prevAutoTemp      = autoTemp;
            bool  prevShutDown      = shutDown;
            float prevFissionRate   = targetFissionRate;
            float prevTurbineOutput = targetTurbineOutput;

            switch (objective.Option.ToLowerInvariant())
            {
            case "powerup":
                shutDown = false;
                if (objective.Override || !autoTemp)
                {
                    //characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
                    if (degreeOfSuccess < 0.5f)
                    {
                        AutoTemp = true;
                    }
                    else
                    {
                        AutoTemp = false;
                        UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
                    }
                }
#if CLIENT
                onOffSwitch.BarScroll            = 0.0f;
                fissionRateScrollBar.BarScroll   = FissionRate / 100.0f;
                turbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
#endif
                break;

            case "shutdown":
#if CLIENT
                onOffSwitch.BarScroll = 1.0f;
#endif
                AutoTemp            = false;
                shutDown            = true;
                targetFissionRate   = 0.0f;
                targetTurbineOutput = 0.0f;
                break;
            }

            if (autoTemp != prevAutoTemp ||
                prevShutDown != shutDown ||
                Math.Abs(prevFissionRate - targetFissionRate) > 1.0f ||
                Math.Abs(prevTurbineOutput - targetTurbineOutput) > 1.0f)
            {
                unsentChanges = true;
            }

            aiUpdateTimer = AIUpdateInterval;

            return(false);
        }
Beispiel #10
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return(false);
            }
            character.AIController.SteeringManager.Reset();
            bool shutDown = objective.Option.Equals("shutdown", StringComparison.OrdinalIgnoreCase);

            IsActive = true;

            if (!shutDown)
            {
                float degreeOfSuccess = DegreeOfSuccess(character);
                float refuelLimit     = 0.3f;
                //characters with insufficient skill levels don't refuel the reactor
                if (degreeOfSuccess > refuelLimit)
                {
                    if (aiUpdateTimer > 0.0f)
                    {
                        aiUpdateTimer -= deltaTime;
                        return(false);
                    }
                    aiUpdateTimer = AIUpdateInterval;
                    // load more fuel if the current maximum output is only 50% of the current load
                    // or if the fuel rod is (almost) deplenished
                    float minCondition = fuelConsumptionRate * MathUtils.Pow2((degreeOfSuccess - refuelLimit) * 2);
                    if (NeedMoreFuel(minimumOutputRatio: 0.5f, minCondition: minCondition))
                    {
                        bool outOfFuel = false;
                        var  container = item.GetComponent <ItemContainer>();
                        if (objective.SubObjectives.None())
                        {
                            var containObjective = AIContainItems <Reactor>(container, character, objective, itemCount: 1, equip: true, removeEmpty: true, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC, dropItemOnDeselected: true);
                            containObjective.Completed += ReportFuelRodCount;
                            containObjective.Abandoned += ReportFuelRodCount;
                            character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);

                            void ReportFuelRodCount()
                            {
                                if (!character.IsOnPlayerTeam)
                                {
                                    return;
                                }
                                if (character.Submarine != Submarine.MainSub)
                                {
                                    return;
                                }
                                int remainingFuelRods = Submarine.MainSub.GetItems(false).Count(i => i.HasTag("reactorfuel") && i.Condition > 1);

                                if (remainingFuelRods == 0)
                                {
                                    character.Speak(TextManager.Get("DialogOutOfFuelRods"), null, 0.0f, "outoffuelrods", 30.0f);
                                    outOfFuel = true;
                                }
                                else if (remainingFuelRods < 3)
                                {
                                    character.Speak(TextManager.Get("DialogLowOnFuelRods"), null, 0.0f, "lowonfuelrods", 30.0f);
                                }
                            }
                        }
                        return(outOfFuel);
                    }
                    else
                    {
                        if (Item.ConditionPercentage <= 0 && AIObjectiveRepairItems.IsValidTarget(Item, character))
                        {
                            if (Item.Repairables.Average(r => r.DegreeOfSuccess(character)) > 0.4f)
                            {
                                objective.AddSubObjective(new AIObjectiveRepairItem(character, Item, objective.objectiveManager, isPriority: true));
                                return(false);
                            }
                            else
                            {
                                character.Speak(TextManager.Get("DialogReactorIsBroken"), identifier: "reactorisbroken", minDurationBetweenSimilar: 30.0f);
                            }
                        }
                        if (TooMuchFuel())
                        {
                            DropFuel(minCondition: 0.1f, maxCondition: 100);
                        }
                        else
                        {
                            DropFuel(minCondition: 0, maxCondition: 0);
                        }
                    }
                }
            }

            if (objective.Override)
            {
                if (lastUser != null && lastUser != character && lastUser != LastAIUser)
                {
                    if (lastUser.SelectedConstruction == item && character.IsOnPlayerTeam)
                    {
                        character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
                    }
                }
            }
            else if (LastUserWasPlayer && lastUser != null && lastUser.TeamID == character.TeamID)
            {
                return(true);
            }

            LastUser = LastAIUser = character;

            bool  prevAutoTemp      = autoTemp;
            bool  prevPowerOn       = _powerOn;
            float prevFissionRate   = targetFissionRate;
            float prevTurbineOutput = targetTurbineOutput;

            if (shutDown)
            {
                PowerOn             = false;
                AutoTemp            = false;
                targetFissionRate   = 0.0f;
                targetTurbineOutput = 0.0f;
                unsentChanges       = true;
                return(true);
            }
            else
            {
                PowerOn = true;
                if (objective.Override || !autoTemp)
                {
                    //characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
                    if (degreeOfSuccess < 0.5f)
                    {
                        AutoTemp = true;
                    }
                    else
                    {
                        AutoTemp = false;
                        UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
                    }
                }
#if CLIENT
                FissionRateScrollBar.BarScroll   = FissionRate / 100.0f;
                TurbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
#endif
                if (autoTemp != prevAutoTemp ||
                    prevPowerOn != _powerOn ||
                    Math.Abs(prevFissionRate - targetFissionRate) > 1.0f ||
                    Math.Abs(prevTurbineOutput - targetTurbineOutput) > 1.0f)
                {
                    unsentChanges = true;
                }
                aiUpdateTimer = AIUpdateInterval;
                return(false);
            }


            void DropFuel(float minCondition, float maxCondition)
            {
                if (item.OwnInventory?.AllItems != null)
                {
                    var container = item.GetComponent <ItemContainer>();
                    foreach (Item item in item.OwnInventory.AllItemsMod)
                    {
                        if (item.ConditionPercentage <= maxCondition && item.ConditionPercentage >= minCondition)
                        {
                            item.Drop(character);
                            break;
                        }
                    }
                }
            }
        }
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            var projectiles = GetLoadedProjectiles();

            if (projectiles.Count == 0 || (projectiles.Count == 1 && objective.Option.ToLowerInvariant() != "fire at will"))
            {
                ItemContainer container = null;
                foreach (MapEntity e in item.linkedTo)
                {
                    var containerItem = e as Item;
                    if (containerItem == null)
                    {
                        continue;
                    }

                    container = containerItem.GetComponent <ItemContainer>();
                    if (container != null)
                    {
                        break;
                    }
                }

                if (container == null || container.ContainableItems.Count == 0)
                {
                    return(true);
                }

                var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Names[0], container);
                containShellObjective.IgnoreAlreadyContainedItems = true;
                objective.AddSubObjective(containShellObjective);
                return(false);
            }
            else if (GetAvailablePower() < powerConsumption)
            {
                var batteries = item.GetConnectedComponents <PowerContainer>();

                float          lowestCharge  = 0.0f;
                PowerContainer batteryToLoad = null;
                foreach (PowerContainer battery in batteries)
                {
                    if (batteryToLoad == null || battery.Charge < lowestCharge)
                    {
                        batteryToLoad = battery;
                        lowestCharge  = battery.Charge;
                    }
                }

                if (batteryToLoad == null)
                {
                    return(true);
                }

                if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f)
                {
                    objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, "", false));
                    return(false);
                }
            }

            //enough shells and power
            Character closestEnemy = null;
            float     closestDist  = 3000.0f;

            foreach (Character enemy in Character.CharacterList)
            {
                //ignore humans and characters that are inside the sub
                if (enemy.IsDead || enemy.SpeciesName == "human" || enemy.AnimController.CurrentHull != null)
                {
                    continue;
                }

                float dist = Vector2.Distance(enemy.WorldPosition, item.WorldPosition);
                if (dist < closestDist)
                {
                    closestEnemy = enemy;
                    closestDist  = dist;
                }
            }

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

            character.CursorPosition = closestEnemy.WorldPosition;
            if (item.Submarine != null)
            {
                character.CursorPosition -= item.Submarine.Position;
            }
            character.SetInput(InputType.Aim, false, true);

            float enemyAngle  = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition);
            float turretAngle = -rotation;

            if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.01f)
            {
                return(false);
            }

            var pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), closestEnemy.SimPosition, null);

            if (pickedBody != null && !(pickedBody.UserData is Limb))
            {
                return(false);
            }

            if (objective.Option.ToLowerInvariant() == "fire at will")
            {
                Use(deltaTime, character);
            }

            return(false);
        }
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            RechargeSpeed = maxRechargeSpeed * 0.5f;

            return(true);
        }
Beispiel #13
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (!(objective.OperateTarget is Gap leak))
            {
                return(true);
            }
            if (leak.Submarine == null)
            {
                return(true);
            }
            Vector2 fromCharacterToLeak = leak.WorldPosition - character.WorldPosition;
            float   dist  = fromCharacterToLeak.Length();
            float   reach = Range + ConvertUnits.ToDisplayUnits(((HumanoidAnimController)character.AnimController).ArmLength);

            //too far away -> consider this done and hope the AI is smart enough to move closer
            if (dist > reach * 2)
            {
                return(true);
            }
            character.AIController.SteeringManager.Reset();
            //steer closer if almost in range
            if (dist > reach)
            {
                if (character.AnimController.InWater)
                {
                    if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
                    {
                        // Swimming inside the sub
                        if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
                        {
                            Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
                            character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
                        }
                        else
                        {
                            character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
                        }
                    }
                    else
                    {
                        // Swimming outside the sub
                        character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
                    }
                }
                else
                {
                    // TODO: use the collider size?
                    if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
                        Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f)
                    {
                        ((HumanoidAnimController)character.AnimController).Crouching = true;
                    }
                    Vector2 standPos = new Vector2(Math.Sign(-fromCharacterToLeak.X), Math.Sign(-fromCharacterToLeak.Y)) / 2;
                    if (leak.IsHorizontal)
                    {
                        standPos.X *= 2;
                        standPos.Y  = 0;
                    }
                    else
                    {
                        standPos.X = 0;
                    }
                    character.AIController.SteeringManager.SteeringSeek(standPos);
                }
            }
            else
            {
                if (dist < reach / 2)
                {
                    // Too close -> steer away
                    character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
                }
                else if (dist <= reach)
                {
                    // In range
                    character.CursorPosition  = leak.Position;
                    character.CursorPosition += VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2);
                    if (character.AnimController.InWater)
                    {
                        var torso = character.AnimController.GetLimb(LimbType.Torso);
                        // Turn facing the target when not moving (handled in the animcontroller if not moving)
                        Vector2 mousePos    = ConvertUnits.ToSimUnits(character.CursorPosition);
                        Vector2 diff        = (mousePos - torso.SimPosition) * character.AnimController.Dir;
                        float   newRotation = MathUtils.VectorToAngle(diff);
                        character.AnimController.Collider.SmoothRotate(newRotation, 5.0f);

                        if (VectorExtensions.Angle(VectorExtensions.Forward(torso.body.TransformedRotation), fromCharacterToLeak) < MathHelper.PiOver4)
                        {
                            // Swim past
                            Vector2 moveDir = leak.IsHorizontal ? Vector2.UnitY : Vector2.UnitX;
                            moveDir *= character.AnimController.Dir;
                            character.AIController.SteeringManager.SteeringManual(deltaTime, moveDir);
                        }
                    }
                }
            }
            if (item.RequireAimToUse)
            {
                character.SetInput(InputType.Aim, false, true);
                sinTime += deltaTime * 5;
            }
            // Press the trigger only when the tool is approximately facing the target.
            Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
            var     angle          = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);

            if (angle < MathHelper.PiOver4)
            {
                // Check that we don't hit any friendlies
                if (Submarine.PickBodies(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionCharacter).None(hit =>
                {
                    if (hit.UserData is Character c)
                    {
                        if (c == character)
                        {
                            return(false);
                        }
                        return(HumanAIController.IsFriendly(character, c));
                    }
                    return(false);
                }))
                {
                    character.SetInput(InputType.Shoot, false, true);
                    Use(deltaTime, character);
                }
            }

            bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
                             (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);

            if (leakFixed && leak.FlowTargetHull != null)
            {
                if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
                {
                    character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f);
                }
                else
                {
                    character.Speak(TextManager.GetWithVariable("DialogLeakFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leakfixed", 10.0f);
                }
            }

            return(leakFixed);
        }
Beispiel #14
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            character.AIController.SteeringManager.Reset();
            if (objective.Override)
            {
                if (user != character && user != null && user.SelectedConstruction == item && character.IsOnPlayerTeam)
                {
                    character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
                }
            }
            user = character;

            if (Item.ConditionPercentage <= 0 && AIObjectiveRepairItems.IsValidTarget(Item, character))
            {
                if (Item.Repairables.Average(r => r.DegreeOfSuccess(character)) > 0.4f)
                {
                    objective.AddSubObjective(new AIObjectiveRepairItem(character, Item, objective.objectiveManager, isPriority: true));
                    return(false);
                }
                else
                {
                    character.Speak(TextManager.Get("DialogNavTerminalIsBroken"), identifier: "navterminalisbroken", minDurationBetweenSimilar: 30.0f);
                }
            }

            if (!AutoPilot)
            {
                unsentChanges = true;
                AutoPilot     = true;
            }
            IncreaseSkillLevel(user, deltaTime);
            switch (objective.Option.ToLowerInvariant())
            {
            case "maintainposition":
                if (objective.Override)
                {
                    SetMaintainPosition();
                }
                break;

            case "navigateback":
                if (Level.IsLoadedOutpost)
                {
                    break;
                }
                if (DockingSources.Any(d => d.Docked))
                {
                    item.SendSignal("1", "toggle_docking");
                }
                if (objective.Override)
                {
                    if (MaintainPos || LevelEndSelected || !LevelStartSelected || navigateTactically)
                    {
                        unsentChanges = true;
                    }
                    SetDestinationLevelStart();
                }
                break;

            case "navigatetodestination":
                if (Level.IsLoadedOutpost)
                {
                    break;
                }
                if (DockingSources.Any(d => d.Docked))
                {
                    item.SendSignal("1", "toggle_docking");
                }
                if (objective.Override)
                {
                    if (MaintainPos || !LevelEndSelected || LevelStartSelected || navigateTactically)
                    {
                        unsentChanges = true;
                    }
                    SetDestinationLevelEnd();
                }
                break;

            case "navigatetactical":
                if (Level.IsLoadedOutpost)
                {
                    break;
                }
                if (DockingSources.Any(d => d.Docked))
                {
                    item.SendSignal("1", "toggle_docking");
                }
                if (objective.Override)
                {
                    if (MaintainPos || LevelEndSelected || LevelStartSelected || !navigateTactically)
                    {
                        unsentChanges = true;
                    }
                    SetDestinationTactical();
                }
                break;
            }
            sonar?.AIOperate(deltaTime, character, objective);
            if (!MaintainPos && showIceSpireWarning && character.IsOnPlayerTeam)
            {
                character.Speak(TextManager.Get("dialogicespirespottedsonar"), null, 0.0f, "icespirespottedsonar", 60.0f);
            }
            return(false);
        }
Beispiel #15
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return(false);
            }

            if (objective.Override)
            {
                HasBeenTuned = false;
            }
            if (HasBeenTuned)
            {
                return(true);
            }

            float targetRatio = string.IsNullOrEmpty(objective.Option) || objective.Option.Equals("charge", StringComparison.OrdinalIgnoreCase) ? aiRechargeTargetRatio : -1;

            if (targetRatio > 0 || float.TryParse(objective.Option, out targetRatio))
            {
                if (Math.Abs(rechargeSpeed - maxRechargeSpeed * targetRatio) > 0.05f)
                {
#if SERVER
                    item.CreateServerEvent(this);
#endif
                    RechargeSpeed = maxRechargeSpeed * targetRatio;
#if CLIENT
                    if (rechargeSpeedSlider != null)
                    {
                        rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f);
                    }
#endif
                    if (character.IsOnPlayerTeam)
                    {
                        character.Speak(TextManager.GetWithVariables("DialogChargeBatteries", new string[2] {
                            "[itemname]", "[rate]"
                        },
                                                                     new string[2] {
                            item.Name, ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString()
                        },
                                                                     new bool[2] {
                            true, false
                        }), null, 1.0f, "chargebattery", 10.0f);
                    }
                }
            }
            else
            {
                if (rechargeSpeed > 0.0f)
                {
#if SERVER
                    item.CreateServerEvent(this);
#endif
                    RechargeSpeed = 0.0f;
#if CLIENT
                    if (rechargeSpeedSlider != null)
                    {
                        rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f);
                    }
#endif
                    if (character.IsOnPlayerTeam)
                    {
                        character.Speak(TextManager.GetWithVariables("DialogStopChargingBatteries", new string[2] {
                            "[itemname]", "[rate]"
                        },
                                                                     new string[2] {
                            item.Name, ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString()
                        },
                                                                     new bool[2] {
                            true, false
                        }), null, 1.0f, "chargebattery", 10.0f);
                    }
                }
            }

            return(true);
        }
Beispiel #16
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return(false);
            }

            IsActive = true;

            float degreeOfSuccess = DegreeOfSuccess(character);

            //characters with insufficient skill levels don't refuel the reactor
            if (degreeOfSuccess > 0.2f)
            {
                //remove used-up fuel from the reactor
                var containedItems = item.ContainedItems;
                foreach (Item item in containedItems)
                {
                    if (item != null && item.Condition <= 0.0f)
                    {
                        item.Drop(character);
                    }
                }

                if (aiUpdateTimer > 0.0f)
                {
                    aiUpdateTimer -= deltaTime;
                    return(false);
                }

                //load more fuel if the current maximum output is only 50% of the current load
                if (NeedMoreFuel(minimumOutputRatio: 0.5f))
                {
                    var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent <ItemContainer>(), objective.objectiveManager)
                    {
                        targetItemCount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1,
                        GetItemPriority = (Item fuelItem) =>
                        {
                            if (fuelItem.ParentInventory?.Owner is Item)
                            {
                                //don't take fuel from other reactors
                                if (((Item)fuelItem.ParentInventory.Owner).GetComponent <Reactor>() != null)
                                {
                                    return(0.0f);
                                }
                            }
                            return(1.0f);
                        }
                    };
                    objective.AddSubObjective(containFuelObjective);

                    character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);

                    aiUpdateTimer = AIUpdateInterval;
                    return(false);
                }
                else if (TooMuchFuel())
                {
                    foreach (Item item in item.ContainedItems)
                    {
                        if (item != null && item.HasTag("reactorfuel"))
                        {
                            if (!character.Inventory.TryPutItem(item, character, allowedSlots: item.AllowedSlots))
                            {
                                item.Drop(character);
                            }
                            break;
                        }
                    }
                }
            }

            if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item)
            {
                character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
            }

            LastUser = character;

            switch (objective.Option.ToLowerInvariant())
            {
            case "powerup":
                shutDown = false;
                //characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
                if (degreeOfSuccess < 0.5f)
                {
                    if (!autoTemp)
                    {
                        unsentChanges = true;
                    }
                    AutoTemp = true;
                }
                else
                {
                    AutoTemp      = false;
                    unsentChanges = true;
                    UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
                }
#if CLIENT
                onOffSwitch.BarScroll            = 0.0f;
                fissionRateScrollBar.BarScroll   = FissionRate / 100.0f;
                turbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
#endif
                break;

            case "shutdown":
#if CLIENT
                onOffSwitch.BarScroll = 1.0f;
#endif
                if (AutoTemp || !shutDown || targetFissionRate > 0.0f || targetTurbineOutput > 0.0f)
                {
                    unsentChanges = true;
                }

                AutoTemp            = false;
                shutDown            = true;
                targetFissionRate   = 0.0f;
                targetTurbineOutput = 0.0f;
                break;
            }

            aiUpdateTimer = AIUpdateInterval;

            return(false);
        }
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            float degreeOfSuccess = DegreeOfSuccess(character);

            //characters with insufficient skill levels don't refuel the reactor
            if (degreeOfSuccess > 0.2f)
            {
                //remove used-up fuel from the reactor
                var containedItems = item.ContainedItems;
                foreach (Item item in containedItems)
                {
                    if (item != null && item.Condition <= 0.0f)
                    {
                        item.Drop();
                    }
                }

                //the temperature is too low and not increasing even though the fission rate is high and cooling low
                // -> we need more fuel
                if (temperature < load * 0.5f && temperatureChange <= 0.0f && fissionRate > 0.9f && coolingRate < 0.1f)
                {
                    var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "Fuel Rod", "reactorfuel" }, item.GetComponent <ItemContainer>());
                    containFuelObjective.MinContainedAmount = containedItems.Count(i => i != null && i.Prefab.NameMatches("Fuel Rod") || i.HasTag("reactorfuel")) + 1;
                    containFuelObjective.GetItemPriority    = (Item fuelItem) =>
                    {
                        if (fuelItem.ParentInventory?.Owner is Item)
                        {
                            //don't take fuel from other reactors
                            if (((Item)fuelItem.ParentInventory.Owner).GetComponent <Reactor>() != null)
                            {
                                return(0.0f);
                            }
                        }
                        return(1.0f);
                    };
                    objective.AddSubObjective(containFuelObjective);

                    return(false);
                }
            }


            switch (objective.Option.ToLowerInvariant())
            {
            case "power up":
                float tempDiff = load - temperature;

                shutDownTemp = Math.Min(load + 1000.0f, 7500.0f);

                //characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
                if (Math.Abs(tempDiff) < 500.0f || degreeOfSuccess < 0.5f)
                {
                    AutoTemp = true;
                }
                else
                {
                    AutoTemp = false;
                    //higher skill levels make the character adjust the temperature faster
                    FissionRate += deltaTime * 100.0f * Math.Sign(tempDiff) * degreeOfSuccess;
                    CoolingRate -= deltaTime * 100.0f * Math.Sign(tempDiff) * degreeOfSuccess;
                }
                break;

            case "shutdown":
                shutDownTemp = 0.0f;
                break;
            }

            return(false);
        }
Beispiel #18
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            Gap leak = objective.OperateTarget as Gap;

            if (leak == null)
            {
                return(true);
            }

            Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
            float   dist           = fromItemToLeak.Length();

            //too far away -> consider this done and hope the AI is smart enough to move closer
            if (dist > Range * 5.0f)
            {
                return(true);
            }

            // TODO: use the collider size?
            if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
                Math.Abs(fromItemToLeak.X) < 100.0f && fromItemToLeak.Y < 0.0f && fromItemToLeak.Y > -150.0f)
            {
                ((HumanoidAnimController)character.AnimController).Crouching = true;
            }

            //steer closer if almost in range
            if (dist > Range)
            {
                Vector2 standPos = leak.IsHorizontal ? new Vector2(Math.Sign(-fromItemToLeak.X), 0.0f) : new Vector2(0.0f, Math.Sign(-fromItemToLeak.Y) * 0.5f);
                standPos = leak.WorldPosition + standPos * Range;
                Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
                character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
            }
            else
            {
                if (dist < Range / 2)
                {
                    // Too close -> steer away
                    character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2);
                }
                else
                {
                    character.AIController.SteeringManager.Reset();
                }
            }

            sinTime += deltaTime;
            character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist);
            if (item.RequireAimToUse)
            {
                character.SetInput(InputType.Aim, false, true);
            }

            // Press the trigger only when the tool is approximately facing the target.
            // If the character is climbing, ignore the check, because we cannot aim while climbing.
            if (VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak) < MathHelper.PiOver4)
            {
                Use(deltaTime, character);
            }
            else
            {
                sinTime -= deltaTime * 2;
            }

            bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
                             (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);

            if (leakFixed && leak.FlowTargetHull != null)
            {
                sinTime = 0;
                if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
                {
                    character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leaksfixed", 10.0f);
                }
                else
                {
                    character.Speak(TextManager.Get("DialogLeakFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leakfixed", 10.0f);
                }
            }

            return(leakFixed);
        }
Beispiel #19
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return(false);
            }

            IsActive = true;

            float degreeOfSuccess = DegreeOfSuccess(character);
            float refuelLimit     = 0.3f;

            //characters with insufficient skill levels don't refuel the reactor
            if (degreeOfSuccess > refuelLimit)
            {
                if (objective.SubObjectives.None())
                {
                    if (!AIDecontainEmptyItems(character, objective, equip: false))
                    {
                        return(false);
                    }
                }

                if (aiUpdateTimer > 0.0f)
                {
                    aiUpdateTimer -= deltaTime;
                    return(false);
                }
                aiUpdateTimer = AIUpdateInterval;

                // load more fuel if the current maximum output is only 50% of the current load
                // or if the fuel rod is (almost) deplenished
                float minCondition = fuelConsumptionRate * MathUtils.Pow((degreeOfSuccess - refuelLimit) * 2, 2);
                if (NeedMoreFuel(minimumOutputRatio: 0.5f, minCondition: minCondition))
                {
                    var container = item.GetComponent <ItemContainer>();
                    if (objective.SubObjectives.None())
                    {
                        int itemCount = item.ContainedItems.Count(i => i != null && container.ContainableItems.Any(ri => ri.MatchesItem(i))) + 1;
                        AIContainItems <Reactor>(container, character, objective, itemCount, equip: false, removeEmpty: true, spawnItemIfNotFound: character.TeamID == Character.TeamType.FriendlyNPC);
                        character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
                    }
                    return(false);
                }
                else if (TooMuchFuel())
                {
                    var container      = item.GetComponent <ItemContainer>();
                    var containedItems = item.OwnInventory?.Items;
                    if (containedItems != null)
                    {
                        foreach (Item item in containedItems)
                        {
                            if (item != null && container.ContainableItems.Any(ri => ri.MatchesItem(item)))
                            {
                                if (!character.Inventory.TryPutItem(item, character, allowedSlots: item.AllowedSlots))
                                {
                                    item.Drop(character);
                                }
                                break;
                            }
                        }
                    }
                }
            }

            if (objective.Override)
            {
                if (lastUser != null && lastUser != character && lastUser != LastAIUser)
                {
                    if (lastUser.SelectedConstruction == item)
                    {
                        character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
                    }
                }
            }
            else if (LastUserWasPlayer)
            {
                return(true);
            }

            LastUser = LastAIUser = character;

            bool  prevAutoTemp      = autoTemp;
            bool  prevPowerOn       = _powerOn;
            float prevFissionRate   = targetFissionRate;
            float prevTurbineOutput = targetTurbineOutput;

            switch (objective.Option.ToLowerInvariant())
            {
            case "powerup":
                PowerOn = true;
                if (objective.Override || !autoTemp)
                {
                    //characters with insufficient skill levels simply set the autotemp on instead of trying to adjust the temperature manually
                    if (degreeOfSuccess < 0.5f)
                    {
                        AutoTemp = true;
                    }
                    else
                    {
                        AutoTemp = false;
                        UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
                    }
                }
#if CLIENT
                FissionRateScrollBar.BarScroll   = FissionRate / 100.0f;
                TurbineOutputScrollBar.BarScroll = TurbineOutput / 100.0f;
#endif
                break;

            case "shutdown":
                PowerOn             = false;
                AutoTemp            = false;
                targetFissionRate   = 0.0f;
                targetTurbineOutput = 0.0f;
                unsentChanges       = true;
                return(true);
            }

            if (autoTemp != prevAutoTemp ||
                prevPowerOn != _powerOn ||
                Math.Abs(prevFissionRate - targetFissionRate) > 1.0f ||
                Math.Abs(prevTurbineOutput - targetTurbineOutput) > 1.0f)
            {
                unsentChanges = true;
            }

            aiUpdateTimer = AIUpdateInterval;

            return(false);
        }
Beispiel #20
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (character.AIController.SelectedAiTarget?.Entity is Character previousTarget &&
                previousTarget.IsDead)
            {
                character?.Speak(TextManager.Get("DialogTurretTargetDead"), null, 0.0f, "killedtarget" + previousTarget.ID, 30.0f);
                character.AIController.SelectTarget(null);
            }

            if (GetAvailableBatteryPower() < powerConsumption)
            {
                var batteries = item.GetConnectedComponents <PowerContainer>();

                float          lowestCharge  = 0.0f;
                PowerContainer batteryToLoad = null;
                foreach (PowerContainer battery in batteries)
                {
                    if (batteryToLoad == null || battery.Charge < lowestCharge)
                    {
                        batteryToLoad = battery;
                        lowestCharge  = battery.Charge;
                    }
                }

                if (batteryToLoad == null)
                {
                    return(true);
                }

                if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f)
                {
                    objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, objective.objectiveManager, option: "", requireEquip: false));
                    return(false);
                }
            }

            int usableProjectileCount = 0;
            int maxProjectileCount    = 0;

            foreach (MapEntity e in item.linkedTo)
            {
                if (!(e is Item projectileContainer))
                {
                    continue;
                }

                var containedItems = projectileContainer.ContainedItems;
                if (containedItems != null)
                {
                    var container = projectileContainer.GetComponent <ItemContainer>();
                    maxProjectileCount += container.Capacity;

                    int projectiles = containedItems.Count(it => it.Condition > 0.0f);
                    usableProjectileCount += projectiles;
                }
            }

            if (usableProjectileCount == 0 || (usableProjectileCount < maxProjectileCount && objective.Option.Equals("fireatwill", StringComparison.OrdinalIgnoreCase)))
            {
                ItemContainer container     = null;
                Item          containerItem = null;
                foreach (MapEntity e in item.linkedTo)
                {
                    containerItem = e as Item;
                    if (containerItem == null)
                    {
                        continue;
                    }
                    container = containerItem.GetComponent <ItemContainer>();
                    if (container != null)
                    {
                        break;
                    }
                }
                if (container == null || container.ContainableItems.Count == 0)
                {
                    return(true);
                }

                if (objective.SubObjectives.None())
                {
                    if (!AIDecontainEmptyItems(character, objective, equip: true, sourceContainer: container))
                    {
                        return(false);
                    }
                }
                if (objective.SubObjectives.None())
                {
                    var loadItemsObjective = AIContainItems <Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true);
                    loadItemsObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
                    character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "loadturret", 30.0f);
                }
                return(false);
            }

            //enough shells and power
            Character closestEnemy = null;
            float     closestDist  = AIRange * AIRange;

            foreach (Character enemy in Character.CharacterList)
            {
                // Ignore dead, friendly, and those that are inside the same sub
                if (enemy.IsDead || !enemy.Enabled || enemy.Submarine == character.Submarine)
                {
                    continue;
                }
                if (HumanAIController.IsFriendly(character, enemy))
                {
                    continue;
                }

                float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition);
                if (dist > closestDist)
                {
                    continue;
                }

                float angle       = -MathUtils.VectorToAngle(enemy.WorldPosition - item.WorldPosition);
                float midRotation = (minRotation + maxRotation) / 2.0f;
                while (midRotation - angle < -MathHelper.Pi)
                {
                    angle -= MathHelper.TwoPi;
                }
                while (midRotation - angle > MathHelper.Pi)
                {
                    angle += MathHelper.TwoPi;
                }

                if (angle < minRotation || angle > maxRotation)
                {
                    continue;
                }

                closestEnemy = enemy;
                closestDist  = dist;
            }

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

            character.AIController.SelectTarget(closestEnemy.AiTarget);

            character.CursorPosition = closestEnemy.WorldPosition;
            if (item.Submarine != null)
            {
                character.CursorPosition -= item.Submarine.Position;
            }

            float enemyAngle  = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition);
            float turretAngle = -rotation;

            if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f)
            {
                return(false);
            }


            Vector2 start = ConvertUnits.ToSimUnits(item.WorldPosition);
            Vector2 end   = ConvertUnits.ToSimUnits(closestEnemy.WorldPosition);

            if (closestEnemy.Submarine != null)
            {
                start -= closestEnemy.Submarine.SimPosition;
                end   -= closestEnemy.Submarine.SimPosition;
            }
            var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel;
            var pickedBody          = Submarine.PickBody(start, end, null, collisionCategories, allowInsideFixture: true,
                                                         customPredicate: (Fixture f) => { return(!item.StaticFixtures.Contains(f)); });

            if (pickedBody == null)
            {
                return(false);
            }
            Character targetCharacter = null;

            if (pickedBody.UserData is Character c)
            {
                targetCharacter = c;
            }
            else if (pickedBody.UserData is Limb limb)
            {
                targetCharacter = limb.character;
            }
            if (targetCharacter != null)
            {
                if (HumanAIController.IsFriendly(character, targetCharacter))
                {
                    // Don't shoot friendly characters
                    return(false);
                }
            }
            else
            {
                if (pickedBody.UserData is ISpatialEntity e)
                {
                    Submarine sub = e.Submarine;
                    if (sub == null)
                    {
                        return(false);
                    }
                    if (sub == Item.Submarine)
                    {
                        return(false);
                    }
                    // Don't shoot non-player submarines, i.e. wrecks or outposts.
                    if (!sub.Info.IsPlayer)
                    {
                        return(false);
                    }
                    // Don't shoot friendly submarines.
                    if (sub.TeamID == Item.Submarine.TeamID)
                    {
                        return(false);
                    }
                }
                else
                {
                    // Hit something else, probably a level wall
                    return(false);
                }
            }
            if (objective.Option.Equals("fireatwill", StringComparison.OrdinalIgnoreCase))
            {
                character?.Speak(TextManager.GetWithVariable("DialogFireTurret", "[itemname]", item.Name, true), null, 0.0f, "fireturret", 5.0f);
                character.SetInput(InputType.Shoot, true, true);
            }
            return(false);
        }
Beispiel #21
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (character.AIController.SelectedAiTarget?.Entity is Character previousTarget &&
                previousTarget.IsDead)
            {
                character?.Speak(TextManager.Get("DialogTurretTargetDead"), null, 0.0f, "killedtarget" + previousTarget.ID, 30.0f);
                character.AIController.SelectTarget(null);
            }

            if (GetAvailablePower() < powerConsumption)
            {
                var batteries = item.GetConnectedComponents <PowerContainer>();

                float          lowestCharge  = 0.0f;
                PowerContainer batteryToLoad = null;
                foreach (PowerContainer battery in batteries)
                {
                    if (batteryToLoad == null || battery.Charge < lowestCharge)
                    {
                        batteryToLoad = battery;
                        lowestCharge  = battery.Charge;
                    }
                }

                if (batteryToLoad == null)
                {
                    return(true);
                }

                if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f)
                {
                    objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, objective.objectiveManager, option: "", requireEquip: false));
                    return(false);
                }
            }

            int usableProjectileCount = 0;
            int maxProjectileCount    = 0;

            foreach (MapEntity e in item.linkedTo)
            {
                if (!(e is Item projectileContainer))
                {
                    continue;
                }

                var containedItems = projectileContainer.ContainedItems;
                if (containedItems != null)
                {
                    var container = projectileContainer.GetComponent <ItemContainer>();
                    maxProjectileCount += container.Capacity;

                    int projectiles = containedItems.Count(it => it.Condition > 0.0f);
                    usableProjectileCount += projectiles;
                }
            }

            if (usableProjectileCount == 0 || (usableProjectileCount < maxProjectileCount && objective.Option.ToLowerInvariant() != "fireatwill"))
            {
                ItemContainer container     = null;
                Item          containerItem = null;
                foreach (MapEntity e in item.linkedTo)
                {
                    containerItem = e as Item;
                    if (containerItem == null)
                    {
                        continue;
                    }

                    container = containerItem.GetComponent <ItemContainer>();
                    if (container != null)
                    {
                        break;
                    }
                }

                if (container == null || container.ContainableItems.Count == 0)
                {
                    return(true);
                }

                if (container.Inventory.Items[0] != null && container.Inventory.Items[0].Condition <= 0.0f)
                {
                    var removeShellObjective = new AIObjectiveDecontainItem(character, container.Inventory.Items[0], container, objective.objectiveManager);
                    objective.AddSubObjective(removeShellObjective);
                }

                var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Identifiers[0], container, objective.objectiveManager);
                character?.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "loadturret", 30.0f);
                containShellObjective.targetItemCount             = usableProjectileCount + 1;
                containShellObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
                objective.AddSubObjective(containShellObjective);
                return(false);
            }

            //enough shells and power
            Character closestEnemy = null;
            float     closestDist  = 3000 * 3000;

            foreach (Character enemy in Character.CharacterList)
            {
                // Ignore friendly and those that are inside the sub
                if (enemy.IsDead || enemy.AnimController.CurrentHull != null || !enemy.Enabled)
                {
                    continue;
                }
                if (HumanAIController.IsFriendly(character, enemy))
                {
                    continue;
                }

                float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition);
                if (dist > closestDist)
                {
                    continue;
                }

                float angle       = -MathUtils.VectorToAngle(enemy.WorldPosition - item.WorldPosition);
                float midRotation = (minRotation + maxRotation) / 2.0f;
                while (midRotation - angle < -MathHelper.Pi)
                {
                    angle -= MathHelper.TwoPi;
                }
                while (midRotation - angle > MathHelper.Pi)
                {
                    angle += MathHelper.TwoPi;
                }

                if (angle < minRotation || angle > maxRotation)
                {
                    continue;
                }

                closestEnemy = enemy;
                closestDist  = dist;
            }

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

            character.AIController.SelectTarget(closestEnemy.AiTarget);

            character.CursorPosition = closestEnemy.WorldPosition;
            if (item.Submarine != null)
            {
                character.CursorPosition -= item.Submarine.Position;
            }

            float enemyAngle  = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition);
            float turretAngle = -rotation;

            if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f)
            {
                return(false);
            }

            var pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), closestEnemy.SimPosition);

            if (pickedBody == null)
            {
                return(false);
            }
            Character target = null;

            if (pickedBody.UserData is Character c)
            {
                target = c;
            }
            else if (pickedBody.UserData is Limb limb)
            {
                target = limb.character;
            }
            if (target == null || HumanAIController.IsFriendly(character, target))
            {
                return(false);
            }

            if (objective.Option.ToLowerInvariant() == "fireatwill")
            {
                character?.Speak(TextManager.GetWithVariable("DialogFireTurret", "[itemname]", item.Name, true), null, 0.0f, "fireturret", 5.0f);
                character.SetInput(InputType.Shoot, true, true);
            }

            return(false);
        }
Beispiel #22
0
 /// <returns>true if the operation was completed</returns>
 public virtual bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
 {
     return(false);
 }
Beispiel #23
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (objective.Override)
            {
                if (user != character && user != null && user.SelectedConstruction == item && character.IsOnPlayerTeam)
                {
                    character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
                }
            }
            user = character;
            if (!AutoPilot)
            {
                unsentChanges = true;
                AutoPilot     = true;
            }
            IncreaseSkillLevel(user, deltaTime);
            switch (objective.Option.ToLowerInvariant())
            {
            case "maintainposition":
                if (objective.Override)
                {
                    if (!MaintainPos)
                    {
                        unsentChanges = true;
                        MaintainPos   = true;
                    }
                    if (!posToMaintain.HasValue)
                    {
                        unsentChanges = true;
                        posToMaintain = controlledSub != null ?
                                        controlledSub.WorldPosition :
                                        item.Submarine == null ? item.WorldPosition : item.Submarine.WorldPosition;
                    }
                }
                break;

            case "navigateback":
                if (Level.IsLoadedOutpost)
                {
                    break;
                }
                if (DockingSources.Any(d => d.Docked))
                {
                    item.SendSignal("1", "toggle_docking");
                }
                if (objective.Override)
                {
                    if (MaintainPos || LevelEndSelected || !LevelStartSelected)
                    {
                        unsentChanges = true;
                    }
                    SetDestinationLevelStart();
                }
                break;

            case "navigatetodestination":
                if (Level.IsLoadedOutpost)
                {
                    break;
                }
                if (DockingSources.Any(d => d.Docked))
                {
                    item.SendSignal("1", "toggle_docking");
                }
                if (objective.Override)
                {
                    if (MaintainPos || !LevelEndSelected || LevelStartSelected)
                    {
                        unsentChanges = true;
                    }
                    SetDestinationLevelEnd();
                }
                break;
            }
            sonar?.AIOperate(deltaTime, character, objective);
            if (!MaintainPos && showIceSpireWarning && character.IsOnPlayerTeam)
            {
                character.Speak(TextManager.Get("dialogicespirespottedsonar"), null, 0.0f, "icespirespottedsonar", 60.0f);
            }
            return(false);
        }