Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let AI engulf even if running out of atp #5729

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 73 additions & 24 deletions src/microbe_stage/systems/MicrobeAISystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor

ref var control = ref entity.Get<MicrobeControl>();

ref var cellHealth = ref entity.Get<Health>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to use the health variable that is already passed into this function?


ref var compoundStorage = ref entity.Get<CompoundStorage>();

var compounds = compoundStorage.Compounds;
Expand Down Expand Up @@ -330,18 +332,30 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
control.SetMucocystState(ref organelles, ref compoundStorage, entity, false);
}

float atpLevel = compounds.GetCompoundAmount(Compound.ATP);

// If this microbe is out of ATP, pick an amount of time to rest
if (compounds.GetCompoundAmount(Compound.ATP) < 1.0f)
if (atpLevel < 1.0f)
{
// Keep the maximum at 95% full, as there is flickering when near full
ai.ATPThreshold = 0.95f * speciesFocus / Constants.MAX_SPECIES_FOCUS;
}

if (ai.ATPThreshold > MathUtils.EPSILON)
{
if (compounds.GetCompoundAmount(Compound.ATP) <
compounds.GetCapacityForCompound(Compound.ATP) * ai.ATPThreshold)
if (atpLevel < compounds.GetCapacityForCompound(Compound.ATP) * ai.ATPThreshold)
{
if (cellHealth.CurrentHealth > 2 * Constants.ENGULF_NO_ATP_DAMAGE)
{
// Even if we are out of ATP and there is microbe nearby, engulf them.
// make sure engulfing doesn't kill the cell
if (CheckForHuntingConditions(ref ai, ref position, ref organelles, ref ourSpecies, ref engulfer,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be a good idea to move this check to after the if (!outOfSomething) { ... } code block? So that if the microbe has all the necessary compounds, but still lacks ATP because of some temporary conditions (like sprinting) it just waits to generate some more ATP instead of hunting

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that maybe the if could be removed entirely? Because the state force code already skips the force if the cell would have so little health that it would die.

ref cellProperties, ref control, ref cellHealth, ref compoundStorage, entity, speciesFocus,
speciesAggression, speciesActivity, speciesOpportunism, strain, random, true,
atpLevel))
return;
}

bool outOfSomething = false;
foreach (var compound in compounds.Compounds)
{
Expand Down Expand Up @@ -467,6 +481,37 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
}
}

// check if species can hunt any prey and if so - engage in chase
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment should too start with a capital letter

bool isHunting = CheckForHuntingConditions(ref ai, ref position, ref organelles, ref ourSpecies, ref engulfer,
ref cellProperties, ref control, ref health, ref compoundStorage, entity, speciesFocus, speciesAggression,
speciesActivity, speciesOpportunism, strain, random, false, atpLevel);
if (isHunting)
return;

// There is no reason to be engulfing at this stage
control.SetStateColonyAware(entity, MicrobeState.Normal);

// Otherwise just wander around and look for compounds
if (!isSessile)
{
SeekCompounds(in entity, ref ai, ref position, ref control, ref organelles, ref absorber, compounds,
speciesActivity, speciesFocus, random);
}
else
{
// This organism is sessile, and will not act until the environment changes
control.SetMoveSpeed(0.0f);
}
}

private bool CheckForHuntingConditions(ref MicrobeAI ai, ref WorldPosition position,
ref OrganelleContainer organelles, ref SpeciesMember ourSpecies,
ref Engulfer engulfer, ref CellProperties cellProperties, ref MicrobeControl control, ref Health health,
ref CompoundStorage compoundStorage, in Entity entity, float speciesFocus, float speciesAggression,
float speciesActivity, float speciesOpportunism, float strain, Random random, bool outOfAtp, float atpLevel)
{
var compounds = compoundStorage.Compounds;

// If there are no chunks, look for living prey to hunt
var possiblePrey = GetNearestPreyItem(ref ai, ref position, ref organelles, ref ourSpecies, ref engulfer,
compounds, speciesFocus, speciesAggression, speciesOpportunism, random);
Expand All @@ -482,32 +527,25 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
{
GD.PrintErr("Microbe AI tried to engage prey with no position: " + e);
ai.FocusedPrey = default;
return;
return false;
}

bool engulfPrey = cellProperties.CanEngulfObject(ref ourSpecies, ref engulfer, possiblePrey) ==
EngulfCheckResult.Ok && position.Position.DistanceSquaredTo(prey) <
10.0f * engulfer.EngulfingSize;

EngagePrey(ref ai, ref control, ref organelles, ref position, compounds, entity, prey, engulfPrey,
speciesAggression, speciesFocus, speciesActivity, strain, random);
return;
}

// There is no reason to be engulfing at this stage
control.SetStateColonyAware(entity, MicrobeState.Normal);
// if out of ATP and the prey is out of reach to engulf, do nothing
Copy link
Member

@dligr dligr Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment should begin with a capital letter too

if (outOfAtp && !engulfPrey)
{
return false;
}

// Otherwise just wander around and look for compounds
if (!isSessile)
{
SeekCompounds(in entity, ref ai, ref position, ref control, ref organelles, ref absorber, compounds,
speciesActivity, speciesFocus, random);
}
else
{
// This organism is sessile, and will not act until the environment changes
control.SetMoveSpeed(0.0f);
EngagePrey(ref ai, ref control, ref organelles, ref position, ref compoundStorage, ref health, entity,
prey, engulfPrey, speciesAggression, speciesFocus, speciesActivity, strain, random, atpLevel);
return true;
}

return false;
}

private (Entity Entity, Vector3 Position, float EngulfSize, CompoundBag Compounds)? GetNearestChunkItem(
Expand Down Expand Up @@ -858,10 +896,21 @@ private void FleeFromPredators(ref WorldPosition position, ref MicrobeAI ai, ref
}

private void EngagePrey(ref MicrobeAI ai, ref MicrobeControl control, ref OrganelleContainer organelles,
ref WorldPosition position, CompoundBag ourCompounds, in Entity entity, Vector3 target, bool engulf,
float speciesAggression, float speciesFocus, float speciesActivity, float strain, Random random)
ref WorldPosition position, ref CompoundStorage compoundStorage, ref Health health, in Entity entity,
Vector3 target, bool engulf, float speciesAggression, float speciesFocus, float speciesActivity,
float strain, Random random, float atpLevel)
{
control.SetStateColonyAware(entity, engulf ? MicrobeState.Engulf : MicrobeState.Normal);
var ourCompounds = compoundStorage.Compounds;

if (atpLevel <= Constants.ENGULF_NO_ATP_TRIGGER_THRESHOLD)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the EnterEngulfModeForcedState call now, but now I'm wondering why the ATP level is checked here? Because EnterEngulfModeForcedState ultimately calls ForceStateApplyIfRequired which only causes problems if ATP is not sufficient, so the AI code doesn't actually need this duplicate check, right?

{
control.EnterEngulfModeForcedState(ref health, ref compoundStorage, entity, Compound.ATP);
hhyyrylainen marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
control.SetStateColonyAware(entity, engulf ? MicrobeState.Engulf : MicrobeState.Normal);
}

ai.TargetPosition = target;
control.LookAtPoint = ai.TargetPosition;
if (CanShootToxin(ourCompounds, speciesFocus))
Expand Down