/// <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; } }
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(); }
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); } }
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(); }
/// <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); }
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(); }
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); } }
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(); }