Skip to content
Open
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
224cb54
AI: Use move to me command if entity has binding agent
IdfbAn Feb 11, 2026
51c70e1
AI Signaling Agent: Add else condition for no command
IdfbAn Feb 11, 2026
8d88abe
MicrobeAISystem.UseSignalingAgent()
IdfbAn Feb 11, 2026
d5ed2a0
Make UseSignalingAgent() return the command
IdfbAn Feb 12, 2026
e491f3d
Make returning no command outside of conditions
IdfbAn Feb 12, 2026
2d9a1c6
Start adding conditions for predation
IdfbAn Feb 12, 2026
a45ef6b
Comply with JetBrains check
IdfbAn Feb 12, 2026
144a74b
Make UseSignalingAgent() set commands directly again
IdfbAn Feb 13, 2026
4d8bc43
Add "or has toxins" to pili condition
IdfbAn Feb 14, 2026
e9f1932
MicrobeControl.Fleeing
IdfbAn Feb 14, 2026
8195e59
Set control.Fleeing in ChooseActions()
IdfbAn Feb 14, 2026
ecca883
Use flee from me command if fleeing
IdfbAn Feb 15, 2026
36de1a4
Remove control.Fleeing = false line
IdfbAn Feb 15, 2026
f5fc4c8
Remove exact chance from comment
IdfbAn Feb 16, 2026
07f62e2
Move Fleeing to MicrobeAI and increment SERIALIZATION_VERSION
IdfbAn Feb 16, 2026
08cbe44
Handle reading Fleeing for SERIALIZATION_VERSION == 1
IdfbAn Feb 16, 2026
c758755
Correct control.Fleeing to ai.Fleeing
IdfbAn Feb 16, 2026
0920682
Add else conditions that set command to none
IdfbAn Feb 16, 2026
74ac805
Add and use AI_MOVE_DISTANCE_SQUARED
IdfbAn Feb 17, 2026
4c1708e
Add and use AI_BECOME_AGGRESSIVE_DISTANCE_SQUARED
IdfbAn Feb 17, 2026
d471a74
Fix indentation
IdfbAn Feb 17, 2026
43032a9
Fix indentation MicrobeAISystem.cs:273
IdfbAn Feb 17, 2026
16a1d7e
Edit ternary operator to comply with JetBrains
IdfbAn Feb 17, 2026
b98eb16
Move flee from me command to outside of UseSignalingAgent()
IdfbAn Feb 18, 2026
880c09f
Remove MicrobeAI.Fleeing as it is now useless
IdfbAn Feb 18, 2026
6340dba
Remove unused parameter
IdfbAn Feb 18, 2026
ef56fc8
Fix signalerPosition
IdfbAn Feb 18, 2026
66cff35
Fix AI use of flee from me command
IdfbAn Feb 18, 2026
8ae66c4
Improve signalerPosition assignment
IdfbAn Feb 23, 2026
3426b61
Use signalExists in signalerPosition condition
IdfbAn Feb 24, 2026
01abd06
Fulfill (some of) Patryk26g's requests
IdfbAn Feb 24, 2026
0301823
Resolve JetBrains check fail and fulfill new Patryk26g requests
IdfbAn Feb 25, 2026
e1d3cc1
Change comment as suggested by hhyyrylainen
IdfbAn Feb 26, 2026
9179fa4
Add condition for sending become aggressive command
IdfbAn Mar 9, 2026
8d134db
Send follow me command if prey is confident enough
IdfbAn Mar 31, 2026
9d5b1f8
Merge branch 'master' into 5118_ai_signaling_agent
IdfbAn Apr 2, 2026
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
4 changes: 4 additions & 0 deletions simulation_parameters/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1277,8 +1277,12 @@ public static class Constants
public const float AI_BASE_MOVEMENT = 1.0f;
public const float AI_ENGULF_STOP_DISTANCE = 0.8f;

public const float AI_BECOME_AGGRESSIVE_DISTANCE_SQUARED = 120 * 120;
public const float AI_FOLLOW_DISTANCE_SQUARED = 60 * 60;
public const float AI_FLEE_DISTANCE_SQUARED = 85 * 85;
public const float AI_MOVE_DISTANCE_SQUARED = 240 * 240;

public const float AI_SIGNALING_CHANCE = 0.05f;

public const float AI_BASE_TOXIN_SHOOT_ANGLE_PRECISION = 5;

Expand Down
83 changes: 71 additions & 12 deletions src/microbe_stage/systems/MicrobeAISystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,16 +263,30 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor

var compounds = compoundStorage.Compounds;

bool signalExists = signaling.ReceivedCommand != MicrobeSignalCommand.None &&
entity.IsAliveAndHas<WorldPosition>();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Isn't entity wrong here? The system should not even run if the entity is dead, so shouldn't this code be checking if ReceivedCommandFromEntity is the one that is alive?

Vector3 signalerPosition = default;
float signalerDistanceSquared = default;

if (signalExists)
{
signalerPosition = signaling.ReceivedCommandFromEntity.Get<WorldPosition>().Position;
signalerDistanceSquared = position.Position.DistanceSquaredTo(signalerPosition);
}

// Adjusted behaviour values (calculated here as these are needed by various methods)
var speciesBehaviour = ourSpecies.Species.Behaviour;
var adjustBehaviourValues = signaling.ReceivedCommand == MicrobeSignalCommand.BecomeAggressive &&
signalerDistanceSquared < Constants.AI_BECOME_AGGRESSIVE_DISTANCE_SQUARED;
Comment on lines +279 to +280
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this needs to check the signalExists safety flag as well because theoretically the received command might be left to aggressive command but then there signaller doesn't exist.


float speciesAggression = speciesBehaviour.Aggression *
(signaling.ReceivedCommand == MicrobeSignalCommand.BecomeAggressive ? 1.5f : 1.0f);
(adjustBehaviourValues ? 1.5f : 1.0f);

float speciesFear = speciesBehaviour.Fear *
(signaling.ReceivedCommand == MicrobeSignalCommand.BecomeAggressive ? 0.75f : 1.0f);
(adjustBehaviourValues ? 0.75f : 1.0f);

float speciesActivity = speciesBehaviour.Activity *
(signaling.ReceivedCommand == MicrobeSignalCommand.BecomeAggressive ? 1.25f : 1.0f);
(adjustBehaviourValues ? 1.25f : 1.0f);

// Adjust activity for night if it is currently night
// TODO: also check if the current species relies on varying compounds (otherwise it shouldn't react to it
Expand Down Expand Up @@ -300,6 +314,12 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
FleeFromPredators(ref position, ref ai, ref control, ref organelles, ref compoundStorage, entity,
predator.Value.Position, predator.Value.Entity, speciesFocus,
speciesActivity, speciesAggression, speciesFear, strain, random);

if (organelles.HasSignalingAgent && random.NextSingle() < Constants.AI_SIGNALING_CHANCE)
{
signaling.QueuedSignalingCommand = MicrobeSignalCommand.FleeFromMe;
}

return;
}

Expand Down Expand Up @@ -377,8 +397,14 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
ai.ATPThreshold = 0.0f;
}

// Use signaling agent if I have any with a small chance per think
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this supposed to be "per tick"?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I hope not because we do not have "ticks" in the game (the closest concept we have is a game simulation update)...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I do mean "think", as in each time AIThink() is called. I guess it should say "per update", then?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think "per think" is perfectly understandable, and I have a feeling that there are existing comments that are of similar format. But for clarity it could be written as "per think method call".

if (organelles.HasSignalingAgent && random.NextSingle() < Constants.AI_SIGNALING_CHANCE)
{
UseSignalingAgent(ref organelles, speciesAggression, ref signaling, random);
}

// Follow received commands if we have them
if (organelles.HasSignalingAgent && signaling.ReceivedCommand != MicrobeSignalCommand.None)
if (organelles.HasSignalingAgent && signalExists)
{
// TODO: tweak the balance between following commands and doing normal behaviours
// TODO: and also probably we want to add some randomness to the positions and speeds based on distance
Expand All @@ -390,8 +416,11 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
// was smelled from
if (signaling.ReceivedCommandFromEntity.IsAliveAndHas<WorldPosition>())
{
ai.MoveToLocation(signaling.ReceivedCommandFromEntity.Get<WorldPosition>().Position,
ref control, entity);
if (signalerDistanceSquared < Constants.AI_MOVE_DISTANCE_SQUARED)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How small is this distance? The signal is literally meant to bump into other members to make using the binding agent easy for the player, so I don't really agree with this change...

{
ai.MoveToLocation(signalerPosition, ref control, entity);
}

return;
}

Expand All @@ -402,9 +431,8 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
{
if (signaling.ReceivedCommandFromEntity.IsAliveAndHas<WorldPosition>())
{
var signalerPosition = signaling.ReceivedCommandFromEntity.Get<WorldPosition>().Position;
if (position.Position.DistanceSquaredTo(signalerPosition) >
Constants.AI_FOLLOW_DISTANCE_SQUARED)
if (signalerDistanceSquared > Constants.AI_FOLLOW_DISTANCE_SQUARED &&
signalerDistanceSquared < Constants.AI_MOVE_DISTANCE_SQUARED)
{
ai.MoveToLocation(signalerPosition, ref control, entity);
}
Expand All @@ -419,9 +447,7 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
{
if (signaling.ReceivedCommandFromEntity.IsAliveAndHas<WorldPosition>())
{
var signalerPosition = signaling.ReceivedCommandFromEntity.Get<WorldPosition>().Position;
if (position.Position.DistanceSquaredTo(signalerPosition) <
Constants.AI_FLEE_DISTANCE_SQUARED)
if (signalerDistanceSquared < Constants.AI_FLEE_DISTANCE_SQUARED)
{
control.SetStateColonyAware(entity, MicrobeState.Normal);
control.SetMoveSpeed(Constants.AI_BASE_MOVEMENT);
Expand Down Expand Up @@ -525,6 +551,39 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
}
}

private void UseSignalingAgent(ref OrganelleContainer organelles, float speciesAggression,
ref CommandSignaler signaling, Random random)
{
var shouldBeAggressive = RollCheck(speciesAggression, Constants.MAX_SPECIES_AGGRESSION, random);
Copy link
Copy Markdown
Contributor

@Accidental-Explorer Accidental-Explorer Feb 26, 2026

Choose a reason for hiding this comment

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

So this, combined with the section below is just deciding whether they should be trying to travel in a group? (Because this is outside specific responses, just passive, right?)

I think this would make sense not just for aggressive predators, but also for some Brave prey.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment looks like it was not marked as solved?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Patryk26g told me to not resolve (his) comments as solved myself.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You should ask for a review again / comment again mentioning their name to ask for further feedback if a reviewer is not responding in a reasonable amount of time to your further changes (I do know from personal experience that it is easy to miss new changes that were related to a review comment I left).


if (organelles.HasBindingAgent)
{
signaling.QueuedSignalingCommand = MicrobeSignalCommand.MoveToMe;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This line seems useless so should be deleted (there's no early exit so we always execute the line signaling.QueuedSignalingCommand = MicrobeSignalCommand.None; after this).

Or alternatively it could be use some logic to detect when cells would be good to group together and would then set this but only if already in binding mode / going into binding mode, as otherwise it is useless to trigger this signal.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Doesn't it make more sense to remove the signaling.QueuedSignalingCommand = MicrobeSignalCommand.None; right at the end of the function?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Well there has to be a some quite common condition to reset the signal as I don't want the AI to be entirely made so that it is assumed that there's almost always a signal. It is better for signals to only be sent occasionally so that the AI just doesn't become a total mess where it is always just forced to respond to other signals.

Also for safety I think most "non-urgent" signals should not be activated if the current cell is receiving another signal as that just adds to the signal noise.

}

if (shouldBeAggressive)
{
foreach (var organelle in organelles.Organelles!)
{
// Has pili or toxins
if (organelle.Definition.HasPilusComponent || organelles.AgentVacuoleCount > 0)
{
signaling.QueuedSignalingCommand = MicrobeSignalCommand.FollowMe;
break;
}

signaling.QueuedSignalingCommand = MicrobeSignalCommand.None;
Comment on lines +570 to +594
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This loop looks very suspicious to me. I think instead what you actually want to do is first setup variables for pilus count, and then do a count of pili, and only then would you do the membersNearEnough calculation as that's an expensive calculation to do each loop iteration so I'm quite sure you didn't meant to put that logic inside this loop.

}
}
else
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this else is unnecesary

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I guess it is right now, but since I want to add more sub-conditions I'll keep it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are you going to do this in this PR? If not, then you should remove it. YAGNI

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, I'm gonna do it in this PR.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So there was unimplemented stuff? Good thing that I didn't spend any more review energy yet...

{
// TODO: should probably add more
signaling.QueuedSignalingCommand = MicrobeSignalCommand.None;
}

signaling.QueuedSignalingCommand = MicrobeSignalCommand.None;
}

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,
Expand Down