Skip to content

Make AI use the signaling agent and add more conditions for responding to commands#6736

Open
IdfbAn wants to merge 36 commits intomasterfrom
5118_ai_signaling_agent
Open

Make AI use the signaling agent and add more conditions for responding to commands#6736
IdfbAn wants to merge 36 commits intomasterfrom
5118_ai_signaling_agent

Conversation

@IdfbAn
Copy link
Copy Markdown
Member

@IdfbAn IdfbAn commented Feb 15, 2026

Brief Description of What This PR Does

This PR makes the microbe AI use the signaling agent and adds more conditions for responding to commands.

Related Issues

Closes #5118
Discussion: https://forum.revolutionarygamesstudio.com/t/how-should-ai-use-the-signaling-agent/1233

Progress Checklist

Note: before starting this checklist the PR should be marked as non-draft.

  • PR author has checked that this PR works as intended and doesn't
    break existing features:
    https://wiki.revolutionarygamesstudio.com/wiki/Testing_Checklist
    (this is important as to not waste the time of Thrive team
    members reviewing this PR)
  • Initial code review passed (this and further items should not be checked by the PR author)
  • Functionality is confirmed working by another person (see above checklist link)
  • Final code review is passed and code conforms to the
    styleguide.

Before merging all CI jobs should finish on this PR without errors, if
there are automatically detected style issues they should be fixed by
the PR author. Merging must follow our
styleguide.

// Use signaling agent if I have any with a chance of 5% per think
if (organelles.HasSignalingAgent && random.NextSingle() < Constants.AI_SIGNALING_CHANCE)
{
UseSignalingAgent(ref organelles, speciesAggression, ref signaling, ref control, random);
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.

It seems like there's only one case where the signalling command is set to None. That means that cells do not stop signalling! That's a very, very major bug in my opinion because eventually I think most cells will just be signalling all the time.

Also I think another key part still missing is that cells should decide to ignore an incoming signalling command in certain situations (like if become aggressive signal is too far away, or a move signal is far away and the cell is not that full on resources).

Without those two things I think the signals will quite effectively destroy any useful AI behaviours as they'll get permanently locked into following the signal of the first species member of them that triggered it.

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.

Do you have suggestions for when it should be set to no command?

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'd say whenever the original condition to set a signal is gone, or like after 15-30 seconds of the cell not feeling like it should send the same signal again. Something like that, but I'm sure someone else more familiar with the AI could come up with a more advanced codnitions.

@IdfbAn IdfbAn marked this pull request as ready for review February 23, 2026 15:46
@IdfbAn IdfbAn requested review from a team February 24, 2026 10:03
Comment on lines +273 to +276
else
{
signalExists = false;
}
Copy link
Copy Markdown
Contributor

@Patryk26g Patryk26g Feb 24, 2026

Choose a reason for hiding this comment

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

is this "else" needed? can do without it, just make "bool signalExists = signaling.ReceivedCommand != MicrobeSignalCommand.None && entity.IsAliveAndHas()". Btw what is " entity.IsAliveAndHas()" used for? Shouldn't it be "signaling.ReceivedCommandFromEntity.IsAliveAndHas()"?

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.

Btw what is " entity.IsAliveAndHas()" used for?

It's so that the game doesn't crash when there isn't a signal.

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 still should be removed... it literally if signal == false then signal = false

private void UseSignalingAgent(ref OrganelleContainer organelles, float speciesAggression,
ref CommandSignaler signaling, Random random)
{
var willBeAggressiveThisTime = RollCheck(speciesAggression, Constants.MAX_SPECIES_AGGRESSION, random);
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.

willBeAggressiveThisTime -> shouldBecomeAggresive

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.

Personally, I think that's an inaccurate name considering what the variable is being used for.

Copy link
Copy Markdown
Contributor

@Patryk26g Patryk26g Feb 24, 2026

Choose a reason for hiding this comment

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

As I said in other comment, future tense variables should be omitted.

signaling.QueuedSignalingCommand = MicrobeSignalCommand.None;
}
}
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...


// Adjusted behaviour values (calculated here as these are needed by various methods)
var speciesBehaviour = ourSpecies.Species.Behaviour;
var willAdjustBehaviourValues = signaling.ReceivedCommand == MicrobeSignalCommand.BecomeAggressive &&
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.

I think we should omit future tense in variables... adjustBehaviourValues is fine. or event better "shouldBeAggresive"

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 know, I never thought about it, but you are right that future tense is not used in variable names customarily. So yeah, this sounds like a good naming change, and I would definitely have named this variable something along the lines of needsToAdjustBehaviour.

bool signalExists = signaling.ReceivedCommand != MicrobeSignalCommand.None &&
entity.IsAliveAndHas<WorldPosition>();
Vector3 signalerPosition = default;
float squaredDistanceToSignaler = default;
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.

squaredDistanceToSignaler -> signalerDistanceSquared

@IdfbAn IdfbAn marked this pull request as draft February 25, 2026 07:53
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".

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).

@IdfbAn IdfbAn marked this pull request as ready for review March 31, 2026 16:25
@IdfbAn IdfbAn requested review from Patryk26g and hhyyrylainen April 2, 2026 11:12
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?

Comment on lines +279 to +280
var adjustBehaviourValues = signaling.ReceivedCommand == MicrobeSignalCommand.BecomeAggressive &&
signalerDistanceSquared < Constants.AI_BECOME_AGGRESSIVE_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.

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.

{
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...


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.

Comment on lines +570 to +594
if (organelle.Definition.HasPilusComponent || organelles.AgentVacuoleCount > 0)
{
var membersNearEnough = 0;
var enoughMembers = (int)speciesAggression / 100;

foreach (var member in speciesMembers!)
{
if (position.Position.DistanceSquaredTo(member.Position)
< Constants.AI_BECOME_AGGRESSIVE_DISTANCE_SQUARED)
{
++membersNearEnough;
}
}

if (membersNearEnough >= enoughMembers)
{
signaling.QueuedSignalingCommand = MicrobeSignalCommand.BecomeAggressive;
break;
}

signaling.QueuedSignalingCommand = MicrobeSignalCommand.FollowMe;
break;
}

signaling.QueuedSignalingCommand = MicrobeSignalCommand.None;
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

AI should know how to use the signaling agent to send commands

5 participants