/// <summary> /// Gets the nearest chunk. And builds a list on chunkList /// </summary> /// <returns>The nearest chunk item.</returns> /// <param name="allChunks">All chunks the AI knows of.</param> private FloatingChunk GetNearestChunkItem(List <FloatingChunk> allChunks) { FloatingChunk chosenChunk = null; Vector3 testPosition = new Vector3(0, 0, 0); bool setPosition = true; // Retrieve nearest potential chunk foreach (var chunk in allChunks) { if ((SpeciesOpportunism == Constants.MAX_SPECIES_OPPORTUNISM) || ((microbe.EngulfSize * (SpeciesOpportunism / Constants.OPPORTUNISM_DIVISOR)) > chunk.Size)) { chunkList.Add(chunk); var thisPosition = chunk.Translation; if (setPosition) { testPosition = thisPosition; setPosition = false; chosenChunk = chunk; } if ((testPosition - microbe.Translation).LengthSquared() > (thisPosition - microbe.Translation).LengthSquared()) { testPosition = thisPosition; chosenChunk = chunk; } } } return(chosenChunk); }
/// <summary> /// For chasing down and eating chunks in various ways /// </summary> /// <param name="chunk">Chunk.</param> /// <param name="allChunks">All chunks.</param> private void DealWithChunks(FloatingChunk chunk, List <FloatingChunk> allChunks) { // Tick the engulf tick ticksSinceLastToggle += 1; // TODO: do something with the chunk compounds // ReSharper disable once NotAccessedVariable CompoundBag compounds; try { // ReSharper disable once RedundantAssignment compounds = chunk.ContainedCompounds; targetPosition = chunk.Translation; } catch (ObjectDisposedException) { // Turn off engulf if chunk is gone targetChunk = null; hasTargetPosition = false; targetChunk = GetNearestChunkItem(allChunks); microbe.EngulfMode = false; // You got a consumption, good job if (!microbe.IsPlayerMicrobe && !microbe.Species.PlayerSpecies) { microbe.SuccessfulScavenge(); } return; } microbe.LookAtPoint = targetPosition; hasTargetPosition = true; // Always set target Position, for use later in AI microbe.MovementDirection = new Vector3(0.0f, 0.0f, -Constants.AI_BASE_MOVEMENT); // Turn on engulfmode if close if ((microbe.Translation - targetPosition).LengthSquared() <= 300 + microbe.EngulfSize * 3.0f && microbe.Compounds.GetCompoundAmount(atp) >= 1.0f && !microbe.EngulfMode && microbe.EngulfSize > Constants.ENGULF_SIZE_RATIO_REQ * chunk.Size && !microbe.Membrane.Type.CellWall) { microbe.EngulfMode = true; ticksSinceLastToggle = 0; } else if ((microbe.Translation - targetPosition).LengthSquared() >= 500 + microbe.EngulfSize * 3.0f && microbe.EngulfMode && ticksSinceLastToggle >= Constants.AI_ENGULF_INTERVAL) { microbe.EngulfMode = false; ticksSinceLastToggle = 0; } }
/// <summary> /// For chasing down and eating chunks in various ways /// </summary> /// <param name="chunk">Chunk.</param> /// <param name="allChunks">All chunks.</param> private void DealWithChunks(FloatingChunk chunk, List <FloatingChunk> allChunks) { // Tick the engulf tick ticksSinceLastToggle += 1; CompoundBag compounds; try { compounds = chunk.ContainedCompounds; targetPosition = chunk.Translation; } catch (ObjectDisposedException) { // Turn off engulf if chunk is gone targetChunk = null; hasTargetPosition = false; targetChunk = GetNearestChunkItem(allChunks); microbe.EngulfMode = false; // You got a consumption, good job if (!microbe.IsPlayerMicrobe && !microbe.Species.PlayerSpecies) { // TODO: fix // MicrobeOperations::alterSpeciesPopulation(species, // CREATURE_SCAVENGE_POPULATION_GAIN, "successful scavenge"); } return; } microbe.LookAtPoint = targetPosition; hasTargetPosition = true; // Always set target Position, for use later in AI microbe.MovementDirection = new Vector3(0.0f, 0.0f, -Constants.AI_BASE_MOVEMENT); // Turn on engulfmode if close if ((microbe.Translation - targetPosition).LengthSquared() <= 300 + microbe.EngulfSize * 3.0f && microbe.Compounds.GetCompoundAmount(atp) >= 1.0f && !microbe.EngulfMode && microbe.EngulfSize > Constants.ENGULF_SIZE_RATIO_REQ * chunk.Size) { microbe.EngulfMode = true; ticksSinceLastToggle = 0; } else if ((microbe.Translation - targetPosition).LengthSquared() >= 500 + microbe.EngulfSize * 3.0f && microbe.EngulfMode && ticksSinceLastToggle >= Constants.AI_ENGULF_INTERVAL) { microbe.EngulfMode = false; ticksSinceLastToggle = 0; } }
/// <summary> /// Clears all the found targets. Currently used for loading from saves /// </summary> public void ClearAfterLoadedFromSave(Microbe newParent) { microbe = newParent; chunkList?.Clear(); predator = null; predatoryMicrobes.Clear(); prey = null; preyMicrobes.Clear(); targetChunk = null; // Probably should clear this preyPegged = false; }
/// <summary> /// A bit on the lighter save properties copying, /// the spawn function used to create this needs to set some stuff beforehand /// </summary> public void ApplyPropertiesFromSave(FloatingChunk chunk) { NodeGroupSaveHelper.CopyGroups(this, chunk); VentPerSecond = chunk.VentPerSecond; Dissolves = chunk.Dissolves; Size = chunk.Size; Damages = chunk.Damages; DeleteOnTouch = chunk.DeleteOnTouch; Mass = chunk.Mass; Radius = chunk.Radius; ChunkScale = chunk.ChunkScale; ContainedCompounds = chunk.ContainedCompounds; Transform = chunk.Transform; LinearVelocity = chunk.LinearVelocity; AngularVelocity = chunk.AngularVelocity; }
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(); }
public void TriggerOnChunkSpawned(FloatingChunk chunk, bool environmental) { OnChunkSpawned?.Invoke(chunk, environmental); }
private FloatingChunk GetNearestChunkItem(List <FloatingChunk> allChunks, List <Microbe> allMicrobes, Random random) { FloatingChunk chosenChunk = null; // If the microbe cannot absorb, no need for this if (microbe.Membrane.Type.CellWall) { return(null); } // Retrieve nearest potential chunk foreach (var chunk in allChunks) { if (microbe.EngulfSize > chunk.Size * Constants.ENGULF_SIZE_RATIO_REQ && (chunk.Translation - microbe.Translation).LengthSquared() <= (20000.0 * SpeciesFocus / Constants.MAX_SPECIES_FOCUS) + 1500.0) { if (chunk.ContainedCompounds.Compounds.Any(x => microbe.Compounds.IsUseful(x.Key))) { if (chosenChunk == null || (chosenChunk.Translation - microbe.Translation).LengthSquared() > (chunk.Translation - microbe.Translation).LengthSquared()) { chosenChunk = chunk; } } } } // Don't bother with chunks when there's a lot of microbes to compete with if (chosenChunk != null) { var rivals = 0; var distanceToChunk = (microbe.Translation - chosenChunk.Translation).LengthSquared(); foreach (var rival in allMicrobes) { if (rival != microbe) { var rivalDistance = (rival.GlobalTransform.origin - chosenChunk.Translation).LengthSquared(); if (rivalDistance < 500.0f && rivalDistance < distanceToChunk) { rivals++; } } } int rivalThreshold; if (SpeciesOpportunism < Constants.MAX_SPECIES_OPPORTUNISM / 3) { rivalThreshold = 1; } else if (SpeciesOpportunism < Constants.MAX_SPECIES_OPPORTUNISM * 2 / 3) { rivalThreshold = 3; } else { rivalThreshold = 5; } // In rare instances, microbes will choose to be much more ambitious if (RollCheck(SpeciesFocus, Constants.MAX_SPECIES_FOCUS, random)) { rivalThreshold *= 2; } if (rivals > rivalThreshold) { chosenChunk = null; } } return(chosenChunk); }