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

Add radioactive chunk miche and make AI to use and omit radioactive chunks #5867

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
8 changes: 8 additions & 0 deletions src/auto-evo/AutoEvoGlobalCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public class AutoEvoGlobalCache
public readonly CompoundConversionEfficiencyPressure TemperatureConversionEfficiencyPressure;
public readonly EnvironmentalCompoundPressure TemperatureCompoundPressure;

public readonly CompoundConversionEfficiencyPressure RadiationConversionEfficiencyPressure;
public readonly ChunkCompoundPressure RadioactiveChunkPressure;

public readonly PredatorRoot PredatorRoot;

public readonly bool HasTemperature;
Expand Down Expand Up @@ -65,6 +68,11 @@ public AutoEvoGlobalCache(WorldGenerationSettings worldSettings)
new CompoundConversionEfficiencyPressure(Compound.Sunlight, Compound.Glucose, 1.0f);
SunlightCompoundPressure = new EnvironmentalCompoundPressure(Compound.Sunlight, Compound.Glucose, 20000, 1.0f);

RadiationConversionEfficiencyPressure =
new CompoundConversionEfficiencyPressure(Compound.Radiation, Compound.ATP, 1.0f);
RadioactiveChunkPressure = new ChunkCompoundPressure("radioactiveChunk",
new LocalizedString("RADIOACTIVE_CHUNK"), Compound.Radiation, Compound.ATP, 1.0f);

TemperatureConversionEfficiencyPressure =
new CompoundConversionEfficiencyPressure(Compound.Temperature, Compound.ATP, 1.0f);
TemperatureCompoundPressure = new EnvironmentalCompoundPressure(Compound.Temperature, Compound.ATP, 100, 1.0f);
Expand Down
13 changes: 13 additions & 0 deletions src/auto-evo/steps/GenerateMiche.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ public Miche GenerateMicheTree(AutoEvoGlobalCache globalCache)
generatedMiche.AddChild(hydrogenSulfideMiche);
}

var hasRadioactiveChunk =
patch.Biome.Chunks.TryGetValue("radioactiveChunk", out var radioactiveChunk) &&
radioactiveChunk.Density > 0;

// Radioactive Chunk
if (hasRadioactiveChunk)
{
var radiationMiche = new Miche(globalCache.RadiationConversionEfficiencyPressure);
radiationMiche.AddChild(new Miche(globalCache.RadioactiveChunkPressure));

generatedMiche.AddChild(radiationMiche);
}

// Sunlight
// TODO: should there be a dynamic energy level requirement rather than an absolute value?
if (patch.Biome.TryGetCompound(Compound.Sunlight, CompoundAmountType.Biome, out var sunlightAmount) &&
Expand Down
156 changes: 133 additions & 23 deletions src/microbe_stage/systems/MicrobeAISystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ public sealed class MicrobeAISystem : AEntitySetSystem<float>, ISpeciesMemberLoc
private readonly List<(Entity Entity, Vector3 Position, float EngulfSize, CompoundBag Compounds)>
chunkDataCache = new();

private readonly List<(Entity Entity, Vector3 Position, CompoundBag Compounds)>
terrainChunkDataCache = new();

private readonly Dictionary<Species, bool> speciesUsingVaryingCompounds = new();
private readonly HashSet<BioProcess> varyingCompoundsTemporary = new();

Expand Down Expand Up @@ -103,9 +106,8 @@ public MicrobeAISystem(IReadonlyCompoundClouds cloudSystem, IDaylightInfo lightI
microbesSet = world.GetEntities().With<WorldPosition>().With<SpeciesMember>()
.With<Health>().With<Engulfer>().With<Engulfable>().Without<AttachedToEntity>().AsSet();

// Engulfables, which are basically all chunks when they aren't cells, and aren't attached so that they
// also aren't eaten already
chunksSet = world.GetEntities().With<Engulfable>().With<WorldPosition>().With<CompoundStorage>()
// Chunks that aren't cells or attached so that they also aren't eaten already
chunksSet = world.GetEntities().With<WorldPosition>().With<CompoundStorage>()
.Without<SpeciesMember>().Without<AttachedToEntity>().AsSet();
}

Expand Down Expand Up @@ -330,6 +332,17 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
control.SetMucocystState(ref organelles, ref compoundStorage, entity, false);
}

var radiationAmount = compounds.GetCompoundAmount(Compound.Radiation);
var radiationFraction = radiationAmount / compounds.GetCapacityForCompound(Compound.Radiation);

if (radiationFraction > Constants.RADIATION_DAMAGE_THRESHOLD * 0.7f)
{
if (RunFromNearestRadioactiveChunk(ref position, ref ai, ref control))
{
return;
}
}

// If this microbe is out of ATP, pick an amount of time to rest
if (compounds.GetCompoundAmount(Compound.ATP) < 1.0f)
{
Expand Down Expand Up @@ -497,6 +510,16 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
// There is no reason to be engulfing at this stage
control.SetStateColonyAware(entity, MicrobeState.Normal);

// If the microbe has radiation protection it means it has melanosomes and can stay near tha radioactive chunks
// to produce ATP
if (organelles.RadiationProtection > 0)
{
if (GoNearRadioactiveChunk(ref position, ref ai, ref control, speciesFocus))
{
return;
}
}

// Otherwise just wander around and look for compounds
if (!isSessile)
{
Expand All @@ -510,6 +533,82 @@ private void ChooseActions(in Entity entity, ref MicrobeAI ai, ref CompoundAbsor
}
}

private (Entity Entity, Vector3 Position, CompoundBag Compounds)? GetNearestRadioactiveChunk(
ref WorldPosition position, float maxDistance)
{
(Entity Entity, Vector3 Position, CompoundBag Compounds)? chosenChunk = null;
float bestFoundChunkDistance = float.MaxValue;

BuildChunksCache();

foreach (var chunk in terrainChunkDataCache)
{
if (!chunk.Compounds.Compounds.Keys.Contains(Compound.Radiation))
{
continue;
}

var distance = (chunk.Position - position.Position).LengthSquared();

if (distance > bestFoundChunkDistance)
continue;

if (distance > maxDistance)
continue;

chosenChunk = chunk;
}

return chosenChunk;
}

private bool RunFromNearestRadioactiveChunk(ref WorldPosition position, ref MicrobeAI ai,
ref MicrobeControl control)
{
var chosenChunk = GetNearestRadioactiveChunk(ref position, 500.0f);

if (chosenChunk == null)
{
return false;
}

var oppositeDirection = position.Position + (position.Position - chosenChunk.Value.Position);

ai.TargetPosition = oppositeDirection;
control.LookAtPoint = ai.TargetPosition;

control.SetMoveSpeedTowardsPoint(ref position, ai.TargetPosition, Constants.AI_BASE_MOVEMENT);
control.Sprinting = true;

return true;
}

private bool GoNearRadioactiveChunk(ref WorldPosition position, ref MicrobeAI ai,
ref MicrobeControl control, float speciesFocus)
{
var maxDistance = 30000.0f * speciesFocus / Constants.MAX_SPECIES_FOCUS + 3000.0f;
var chosenChunk = GetNearestRadioactiveChunk(ref position, maxDistance);

if (chosenChunk == null)
{
return false;
}

// If the microbe is close to the chunk it doesn't need to go any closer
if (position.Position.DistanceSquaredTo(chosenChunk.Value.Position) < 700.0f)
{
control.SetMoveSpeed(0.0f);
return true;
}

ai.TargetPosition = chosenChunk.Value.Position;
control.LookAtPoint = ai.TargetPosition;

control.SetMoveSpeedTowardsPoint(ref position, ai.TargetPosition, Constants.AI_BASE_MOVEMENT);

return true;
}

private (Entity Entity, Vector3 Position, float EngulfSize, CompoundBag Compounds)? GetNearestChunkItem(
in Entity entity, ref Engulfer engulfer, ref MicrobeControl control, ref WorldPosition position,
CompoundBag ourCompounds, float speciesFocus, float speciesOpportunism, Random random, bool ironEater,
Expand Down Expand Up @@ -1320,34 +1419,45 @@ private void BuildChunksCache()
// To allow multithreaded AI access safely
lock (chunkDataCache)
{
if (chunkCacheBuilt)
return;

foreach (ref readonly var chunk in chunksSet.GetEntities())
lock (terrainChunkDataCache)
{
if (chunk.Has<TimedLife>())
if (chunkCacheBuilt)
return;

foreach (ref readonly var chunk in chunksSet.GetEntities())
{
// Ignore already despawning chunks
ref var timed = ref chunk.Get<TimedLife>();
if (chunk.Has<TimedLife>())
{
// Ignore already despawning chunks
ref var timed = ref chunk.Get<TimedLife>();

if (timed.TimeToLiveRemaining <= 0)
continue;
}
if (timed.TimeToLiveRemaining <= 0)
continue;
}

// Ignore chunks that wouldn't yield any useful compounds when absorbing
ref var compounds = ref chunk.Get<CompoundStorage>();
// Ignore chunks that wouldn't yield any useful compounds when absorbing
ref var compounds = ref chunk.Get<CompoundStorage>();

if (!compounds.Compounds.HasAnyCompounds())
continue;
if (!compounds.Compounds.HasAnyCompounds())
continue;

// TODO: determine if it is a good idea to resolve this data here immediately
ref var position = ref chunk.Get<WorldPosition>();
ref var engulfable = ref chunk.Get<Engulfable>();
// TODO: determine if it is a good idea to resolve this data here immediately
ref var position = ref chunk.Get<WorldPosition>();

chunkDataCache.Add((chunk, position.Position, engulfable.AdjustedEngulfSize, compounds.Compounds));
}
if (chunk.Has<Engulfable>())
{
ref var engulfable = ref chunk.Get<Engulfable>();
chunkDataCache.Add((chunk, position.Position, engulfable.AdjustedEngulfSize,
compounds.Compounds));
}
else
{
terrainChunkDataCache.Add((chunk, position.Position, compounds.Compounds));
}
}

chunkCacheBuilt = true;
chunkCacheBuilt = true;
}
}
}

Expand Down