Exemple #1
0
 /// <summary>
 ///   Anything held between calls of the think() method has a chance of having been disposed elsewhere.
 ///   This sets anything disposed to null to prevent errors. This can probably be removed when issue
 ///   https://github.com/Revolutionary-Games/Thrive/issues/2029 is fixed
 /// </summary>
 private void ClearDisposedReferences(MicrobeAICommonData data)
 {
     if (!data.AllMicrobes.Contains(focusedPrey))
     {
         focusedPrey = null;
     }
 }
Exemple #2
0
    public void Process(float delta)
    {
        var nodes = worldRoot.GetTree().GetNodesInGroup(Constants.AI_GROUP);

        // TODO: it would be nice to only rebuild these lists if some AI think interval has elapsed and these are needed
        var allMicrobes = worldRoot.GetTree().GetNodesInGroup(Constants.AI_TAG_MICROBE);
        var allChunks   = worldRoot.GetTree().GetNodesInGroup(Constants.AI_TAG_CHUNK);

        var data = new MicrobeAICommonData(allMicrobes.Cast <Microbe>().ToList(),
                                           allChunks.Cast <FloatingChunk>().ToList());

        // The objects are processed here in order to take advantage of threading
        var executor = TaskExecutor.Instance;

        for (int i = 0; i < nodes.Count; i += Constants.MICROBE_AI_OBJECTS_PER_TASK)
        {
            int start = i;

            var task = new Task(() =>
            {
                var random = new Random();
                for (int a = start;
                     a < start + Constants.MICROBE_AI_OBJECTS_PER_TASK && a < nodes.Count; ++a)
                {
                    RunAIFor(nodes[a] as IMicrobeAI, delta, random, data);
                }
            });

            tasks.Add(task);
        }

        // Start and wait for tasks to finish
        executor.RunTasks(tasks);
        tasks.Clear();
    }
Exemple #3
0
    private void ChooseActions(Random random, MicrobeAICommonData data)
    {
        if (microbe.IsBeingEngulfed)
        {
            SetMoveSpeed(Constants.AI_BASE_MOVEMENT);
        }

        // If nothing is engulfing me right now, see if there's something that might want to hunt me
        // TODO: https://github.com/Revolutionary-Games/Thrive/issues/2323
        Vector3?predator = GetNearestPredatorItem(data.AllMicrobes)?.GlobalTransform.origin;

        if (predator.HasValue &&
            DistanceFromMe(predator.Value) < (1500.0 * SpeciesFear / Constants.MAX_SPECIES_FEAR))
        {
            FleeFromPredators(random, predator.Value);
            return;
        }

        // If there are no threats, look for a chunk to eat
        if (!microbe.Species.MembraneType.CellWall)
        {
            Vector3?targetChunk = GetNearestChunkItem(data.AllChunks, data.AllMicrobes, random)?.Translation;
            if (targetChunk.HasValue)
            {
                PursueAndConsumeChunks(targetChunk.Value, random);
                return;
            }
        }

        // If there are no chunks, look for living prey to hunt
        var possiblePrey = GetNearestPreyItem(data.AllMicrobes);

        if (possiblePrey != null)
        {
            bool engulfPrey = microbe.CanEngulf(possiblePrey) &&
                              DistanceFromMe(possiblePrey.GlobalTransform.origin) < 10.0f * microbe.EngulfSize;
            Vector3?prey = possiblePrey.GlobalTransform.origin;

            EngagePrey(prey.Value, random, engulfPrey);
            return;
        }

        // Otherwise just wander around and look for compounds
        if (SpeciesActivity > Constants.MAX_SPECIES_ACTIVITY / 10)
        {
            RunAndTumble(random);
        }
        else
        {
            // This organism is sessile, and will not act until the environment changes
            SetMoveSpeed(0.0f);
        }
    }
Exemple #4
0
    public void Think(float delta, Random random, MicrobeAICommonData data)
    {
        _ = delta;

        ClearDisposedReferences(data);

        Vector3?predator    = GetNearestPredatorItem(data.AllMicrobes)?.Translation;
        Vector3?targetChunk = GetNearestChunkItem(data.AllChunks, data.AllMicrobes, random)?.Translation;

        Vector3?prey         = null;
        bool    engulfPrey   = false;
        var     possiblePrey = GetNearestPreyItem(data.AllMicrobes);

        if (possiblePrey != null)
        {
            engulfPrey = possiblePrey.EngulfSize * Constants.ENGULF_SIZE_RATIO_REQ <=
                         microbe.EngulfSize && DistanceFromMe(possiblePrey.Translation) < 10.0f * microbe.EngulfSize;
            prey = possiblePrey.Translation;
        }

        if (microbe.IsBeingEngulfed)
        {
            SetMoveSpeed(Constants.AI_BASE_MOVEMENT);
        }
        else if (predator.HasValue &&
                 DistanceFromMe(predator.Value) < (1500.0 * SpeciesFear / Constants.MAX_SPECIES_FEAR))
        {
            FleeFromPredators(random, predator.Value);
        }
        else if (targetChunk.HasValue)
        {
            PursueAndConsumeChunks(targetChunk.Value, random);
        }
        else if (prey.HasValue)
        {
            EngagePrey(prey.Value, random, engulfPrey);
        }
        else
        {
            if (SpeciesActivity > Constants.MAX_SPECIES_ACTIVITY / 10)
            {
                RunAndTumble(random);
            }
            else
            {
                // This organism is sessile, and will not act until the environment changes
                SetMoveSpeed(0.0f);
            }
        }

        // Clear the absorbed compounds for run and rumble
        microbe.TotalAbsorbedCompounds.Clear();
    }
    private void ChooseActions(Random random, MicrobeAICommonData data)
    {
        if (microbe.IsBeingEngulfed)
        {
            SetMoveSpeed(Constants.AI_BASE_MOVEMENT);
        }

        // If nothing is engulfing me right now, see if there's something that might want to hunt me
        Vector3?predator = GetNearestPredatorItem(data.AllMicrobes)?.Translation;

        if (predator.HasValue &&
            DistanceFromMe(predator.Value) < (1500.0 * SpeciesFear / Constants.MAX_SPECIES_FEAR))
        {
            FleeFromPredators(random, predator.Value);
            return;
        }

        // If there are no threats, look for a chunk to eat that isn't running away
        Vector3?targetChunk = GetNearestChunkItem(data.AllChunks, data.AllMicrobes, random)?.Translation;

        if (targetChunk.HasValue)
        {
            PursueAndConsumeChunks(targetChunk.Value, random);
            return;
        }

        // If there are no chunks, look for living prey to hunt
        var possiblePrey = GetNearestPreyItem(data.AllMicrobes);

        if (possiblePrey != null)
        {
            bool engulfPrey = possiblePrey.EngulfSize * Constants.ENGULF_SIZE_RATIO_REQ <=
                              microbe.EngulfSize && DistanceFromMe(possiblePrey.Translation) < 10.0f * microbe.EngulfSize;
            Vector3?prey = possiblePrey.Translation;

            EngagePrey(prey.Value, random, engulfPrey);
            return;
        }

        // Otherwise just wander around and look for compounds
        if (SpeciesActivity > Constants.MAX_SPECIES_ACTIVITY / 10)
        {
            RunAndTumble(random);
        }
        else
        {
            // This organism is sessile, and will not act until the environment changes
            SetMoveSpeed(0.0f);
        }
    }
    public void Think(float delta, Random random, MicrobeAICommonData data)
    {
        _ = delta;

        // Disable most AI in a colony
        if (microbe.ColonyParent != null)
        {
            return;
        }

        ClearDisposedReferences(data);

        ChooseActions(random, data);

        // Clear the absorbed compounds for run and rumble
        microbe.TotalAbsorbedCompounds.Clear();
    }
Exemple #7
0
    /// <summary>
    ///   Main AI think function for cells
    /// </summary>
    /// <param name="ai">The thing with AI interface implemented</param>
    /// <param name="delta">Passed time</param>
    /// <param name="random">Randomness source</param>
    /// <param name="data">Common data for AI agents, should not be modified</param>
    private void RunAIFor(IMicrobeAI ai, float delta, Random random, MicrobeAICommonData data)
    {
        if (ai == null)
        {
            GD.PrintErr("A node has been put in the ai group " +
                        "but it isn't derived from IMicrobeAI");
            return;
        }

        // Limit how often the AI is run
        ai.TimeUntilNextAIUpdate -= delta;

        if (ai.TimeUntilNextAIUpdate > 0)
        {
            return;
        }

        ai.TimeUntilNextAIUpdate = Constants.MICROBE_AI_THINK_INTERVAL;

        ai.AIThink(delta, random, data);
    }
Exemple #8
0
    public void Think(float delta, Random random, MicrobeAICommonData data)
    {
        _ = delta;

        // Disable most AI in a colony
        if (microbe.ColonyParent != null)
        {
            return;
        }

        ChooseActions(random, data);

        // Store the absorbed compounds for run and rumble
        previouslyAbsorbedCompounds.Clear();
        foreach (var compound in microbe.TotalAbsorbedCompounds)
        {
            previouslyAbsorbedCompounds[compound.Key] = compound.Value;
        }

        // We clear here for update, this is why we stored above!
        microbe.TotalAbsorbedCompounds.Clear();
    }
Exemple #9
0
    public void AIThink(float delta, Random random, MicrobeAICommonData data)
    {
        if (IsPlayerMicrobe)
        {
            throw new InvalidOperationException("AI can't run on the player microbe");
        }

        if (Dead)
        {
            return;
        }

        try
        {
            ai.Think(delta, random, data);
        }
#pragma warning disable CA1031 // AI needs to be boxed good
        catch (Exception e)
#pragma warning restore CA1031
        {
            GD.PrintErr("Microbe AI failure! " + e);
        }
    }
Exemple #10
0
    public void Think(float delta, Random random, MicrobeAICommonData data)
    {
        _ = delta;

        // SetRandomTargetAndSpeed(random);

        // Clear the lists
        predatoryMicrobes.Clear();
        preyMicrobes.Clear();
        chunkList.Clear();

        prey = null;

        // 30 seconds about
        if (boredom == (int)random.Next(SpeciesFocus * 2, 1000.0f + SpeciesFocus * 2))
        {
            // Occasionally you need to reevaluate things
            boredom = 0;
            if (RollCheck(SpeciesActivity, 400, random))
            {
                lifeState = LifeState.PLANTLIKE_STATE;
            }
            else
            {
                lifeState = LifeState.NEUTRAL_STATE;
            }
        }
        else
        {
            boredom++;
        }

        switch (lifeState)
        {
        case LifeState.PLANTLIKE_STATE:
            // This ai would ideally just sit there, until it sees a nice opportunity pop-up unlike neutral,
            // which wanders randomly (has a gather chance) until something interesting pops up
            break;

        case LifeState.NEUTRAL_STATE:
        {
            // Before these would run every time, now they just run for the states that need them.
            boredom    = 0;
            preyPegged = false;
            prey       = null;
            if (predator == null)
            {
                GetNearestPredatorItem(data.AllMicrobes);
            }

            // Peg your prey
            if (!preyPegged)
            {
                prey = null;
                prey = GetNearestPreyItem(data.AllMicrobes);
                if (prey != null)
                {
                    preyPegged = true;
                }
            }

            if (targetChunk == null)
            {
                targetChunk = GetNearestChunkItem(data.AllChunks);
            }

            EvaluateEnvironment(random);
            break;
        }

        case LifeState.GATHERING_STATE:
        {
            // In this state you gather compounds
            if (RollCheck(SpeciesOpportunism, 400.0f, random))
            {
                lifeState = LifeState.SCAVENGING_STATE;
                boredom   = 0;
            }
            else
            {
                DoRunAndTumble(random);
            }

            break;
        }

        case LifeState.FLEEING_STATE:
        {
            if (predator == null)
            {
                GetNearestPredatorItem(data.AllMicrobes);
            }

            // In this state you run from predatory microbes
            if (predator != null)
            {
                DealWithPredators(random);
            }
            else
            {
                if (RollCheck(SpeciesActivity, 400, random))
                {
                    lifeState = LifeState.PLANTLIKE_STATE;
                    boredom   = 0;
                }
                else
                {
                    lifeState = LifeState.NEUTRAL_STATE;
                }
            }

            break;
        }

        case LifeState.PREDATING_STATE:
        {
            // Peg your prey
            if (!preyPegged)
            {
                prey = null;
                prey = GetNearestPreyItem(data.AllMicrobes);
                if (prey != null)
                {
                    preyPegged = true;
                }
            }

            if (preyPegged && prey != null)
            {
                DealWithPrey(data.AllMicrobes, random);
            }
            else
            {
                if (RollCheck(SpeciesActivity, 400, random))
                {
                    lifeState = LifeState.PLANTLIKE_STATE;
                    boredom   = 0;
                }
                else
                {
                    lifeState = LifeState.NEUTRAL_STATE;
                }
            }

            break;
        }

        case LifeState.SCAVENGING_STATE:
        {
            if (targetChunk == null)
            {
                targetChunk = GetNearestChunkItem(data.AllChunks);
            }

            if (targetChunk != null)
            {
                DealWithChunks(targetChunk, data.AllChunks);
            }
            else
            {
                if (!RollCheck(SpeciesOpportunism, 400, random))
                {
                    lifeState = LifeState.NEUTRAL_STATE;
                    boredom   = 0;
                }
                else
                {
                    lifeState = LifeState.SCAVENGING_STATE;
                }
            }

            break;
        }
        }

        // Run reflexes
        DoReflexes();

        // Clear the absorbed compounds for run and rumble
        microbe.TotalAbsorbedCompounds.Clear();
    }