demo2
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Demo2.ECS",
|
||||
"rootNamespace": "Demo2.ECS",
|
||||
"references": [
|
||||
"Demo2.Domain",
|
||||
"Unity.Entities",
|
||||
"Unity.Collections",
|
||||
"Unity.Mathematics",
|
||||
"Unity.Burst"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e97a30deb43db644d974377bb7c70d46
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,127 @@
|
||||
#nullable enable
|
||||
|
||||
using Demo2.Domain;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
namespace Demo2.ECS
|
||||
{
|
||||
public struct Demo2SimulationState : IComponentData
|
||||
{
|
||||
public long Tick;
|
||||
public ulong Seed;
|
||||
public Demo2Phase Phase;
|
||||
public int Cycle;
|
||||
public int NextStableId;
|
||||
public int RuleAppliedCycle;
|
||||
public ulong StateHash;
|
||||
public int SpeedMultiplier;
|
||||
}
|
||||
|
||||
public struct Demo2EconomyState : IComponentData
|
||||
{
|
||||
public long Money;
|
||||
public long Payload;
|
||||
public long MultiplierMilli;
|
||||
public long Energy;
|
||||
public long ExcessFirepower;
|
||||
}
|
||||
|
||||
public struct Demo2StableId : IComponentData { public int Value; }
|
||||
public struct Demo2GridPosition : IComponentData { public short X; public short Y; }
|
||||
|
||||
public struct Demo2MachineState : IComponentData
|
||||
{
|
||||
public Demo2MachineKind Kind;
|
||||
public byte Rotation;
|
||||
public byte Enabled;
|
||||
public int RecipeId;
|
||||
public int ProgressTicks;
|
||||
public int ProcessTicks;
|
||||
public int StoredInput;
|
||||
public int Capacity;
|
||||
public int PendingOutputs;
|
||||
public int Faults;
|
||||
}
|
||||
|
||||
public struct Demo2RecipeState : IComponentData
|
||||
{
|
||||
public Demo2MaterialKind InputA;
|
||||
public Demo2MaterialKind InputB;
|
||||
public Demo2MaterialKind Output;
|
||||
public int InputCountA;
|
||||
public int InputCountB;
|
||||
public int OutputCount;
|
||||
}
|
||||
|
||||
public struct Demo2ConveyorState : IComponentData
|
||||
{
|
||||
public byte Direction;
|
||||
public int Occupancy;
|
||||
public int Capacity;
|
||||
}
|
||||
|
||||
public struct Demo2MaterialState : IComponentData
|
||||
{
|
||||
public Demo2MaterialKind Kind;
|
||||
public int Quantity;
|
||||
public int SubCellProgress;
|
||||
public byte Direction;
|
||||
}
|
||||
|
||||
public struct Demo2TurretAmmo : IComponentData { public long Value; }
|
||||
public struct Demo2CoreHealth : IComponentData { public int Value; }
|
||||
|
||||
public struct Demo2CycleStats : IComponentData
|
||||
{
|
||||
public long Extracted;
|
||||
public long Transported;
|
||||
public long Processed;
|
||||
public long AmmoProduced;
|
||||
public int CompletedSameRecipeStreak;
|
||||
public int LastRecipeId;
|
||||
public int Faults;
|
||||
public int BeltOccupancy;
|
||||
public int BeltCapacity;
|
||||
public byte IronFuelAdjacent;
|
||||
public byte FirstBatch;
|
||||
public int DuplicateNextOutputs;
|
||||
}
|
||||
|
||||
[InternalBufferCapacity(32)]
|
||||
public struct Demo2BuildCommandElement : IBufferElementData
|
||||
{
|
||||
public long Sequence;
|
||||
public FixedString64Bytes IdempotencyToken;
|
||||
public FixedString32Bytes PlayerId;
|
||||
public Demo2MachineKind Kind;
|
||||
public short X;
|
||||
public short Y;
|
||||
public byte Rotation;
|
||||
public int RecipeId;
|
||||
public byte Remove;
|
||||
}
|
||||
|
||||
[InternalBufferCapacity(64)]
|
||||
public struct Demo2ProcessedCommandToken : IBufferElementData { public FixedString64Bytes Value; }
|
||||
|
||||
[InternalBufferCapacity(32)]
|
||||
public struct Demo2RuleInstruction : IBufferElementData
|
||||
{
|
||||
public Demo2RuleOpcode Opcode;
|
||||
public Demo2RuleCondition Condition;
|
||||
public long Operand;
|
||||
public int Slot;
|
||||
}
|
||||
|
||||
public struct Demo2RuleInstructionBlob
|
||||
{
|
||||
public Demo2RuleOpcode Opcode;
|
||||
public Demo2RuleCondition Condition;
|
||||
public long Operand;
|
||||
public int Slot;
|
||||
}
|
||||
|
||||
public struct Demo2RuleProgramBlob { public BlobArray<Demo2RuleInstructionBlob> Instructions; }
|
||||
public struct Demo2RuleProgram : IComponentData { public BlobAssetReference<Demo2RuleProgramBlob> Value; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e6df350c356cf046b48a756e864df3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,198 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Demo2.Domain;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
namespace Demo2.ECS
|
||||
{
|
||||
public sealed class Demo2EcsWorld : IDisposable
|
||||
{
|
||||
private readonly World _world;
|
||||
private readonly Demo2SimulationSystemGroup _group;
|
||||
private BlobAssetReference<Demo2RuleProgramBlob> _ruleBlob;
|
||||
|
||||
public World World => _world;
|
||||
public EntityManager EntityManager => _world.EntityManager;
|
||||
public Entity Singleton { get; }
|
||||
|
||||
public Demo2EcsWorld(ulong seed, IReadOnlyList<Demo2RuleDefinition>? orderedRules = null)
|
||||
{
|
||||
_world = new World("Demo2 Rule Factory", WorldFlags.Game);
|
||||
_group = _world.CreateSystemManaged<Demo2SimulationSystemGroup>();
|
||||
Add<Demo2BuildCommandSystem>();
|
||||
Add<Demo2ExtractionSystem>();
|
||||
Add<Demo2ConveyorSystem>();
|
||||
Add<Demo2InputAllocationSystem>();
|
||||
Add<Demo2ProcessingSystem>();
|
||||
Add<Demo2OutputSystem>();
|
||||
Add<Demo2RuleTriggerSystem>();
|
||||
Add<Demo2TurretLoadingSystem>();
|
||||
Add<Demo2CycleStatisticsSystem>();
|
||||
Add<Demo2StateHashSystem>();
|
||||
_group.SortSystems();
|
||||
|
||||
Singleton = EntityManager.CreateEntity(
|
||||
typeof(Demo2SimulationState), typeof(Demo2EconomyState), typeof(Demo2CoreHealth),
|
||||
typeof(Demo2CycleStats), typeof(Demo2BuildCommandElement), typeof(Demo2ProcessedCommandToken),
|
||||
typeof(Demo2RuleInstruction), typeof(Demo2RuleProgram));
|
||||
EntityManager.SetName(Singleton, "Demo2 Simulation Singleton");
|
||||
EntityManager.SetComponentData(Singleton, new Demo2SimulationState
|
||||
{
|
||||
Seed = seed,
|
||||
Phase = Demo2Phase.Build,
|
||||
Cycle = 1,
|
||||
SpeedMultiplier = 1,
|
||||
RuleAppliedCycle = -1
|
||||
});
|
||||
EntityManager.SetComponentData(Singleton, new Demo2EconomyState { Money = 500, MultiplierMilli = SaturatingMath.Scale });
|
||||
EntityManager.SetComponentData(Singleton, new Demo2CoreHealth { Value = 3 });
|
||||
SetRules(orderedRules ?? Array.Empty<Demo2RuleDefinition>());
|
||||
CreateStarterLayout();
|
||||
Tick();
|
||||
}
|
||||
|
||||
public void Tick() => _group.Update();
|
||||
|
||||
public void QueueBuild(Demo2BuildCommand command)
|
||||
{
|
||||
EntityManager.GetBuffer<Demo2BuildCommandElement>(Singleton).Add(new Demo2BuildCommandElement
|
||||
{
|
||||
Sequence = command.Sequence,
|
||||
IdempotencyToken = command.IdempotencyToken,
|
||||
PlayerId = command.PlayerId,
|
||||
Kind = command.Kind,
|
||||
X = (short)command.X,
|
||||
Y = (short)command.Y,
|
||||
Rotation = command.Rotation,
|
||||
RecipeId = command.RecipeId,
|
||||
Remove = command.Remove ? (byte)1 : (byte)0
|
||||
});
|
||||
}
|
||||
|
||||
public void SetPhase(Demo2Phase phase)
|
||||
{
|
||||
var state = EntityManager.GetComponentData<Demo2SimulationState>(Singleton);
|
||||
state.Phase = phase;
|
||||
EntityManager.SetComponentData(Singleton, state);
|
||||
}
|
||||
|
||||
public void SetCycle(int cycle)
|
||||
{
|
||||
var state = EntityManager.GetComponentData<Demo2SimulationState>(Singleton);
|
||||
state.Cycle = cycle;
|
||||
EntityManager.SetComponentData(Singleton, state);
|
||||
}
|
||||
|
||||
public void SetRules(IReadOnlyList<Demo2RuleDefinition> orderedRules)
|
||||
{
|
||||
if (_ruleBlob.IsCreated) _ruleBlob.Dispose();
|
||||
var flattened = new List<Demo2RuleInstructionBlob>();
|
||||
var buffer = EntityManager.GetBuffer<Demo2RuleInstruction>(Singleton);
|
||||
buffer.Clear();
|
||||
for (var slot = 0; slot < orderedRules.Count; slot++)
|
||||
foreach (var instruction in orderedRules[slot].Instructions)
|
||||
{
|
||||
var value = new Demo2RuleInstructionBlob { Opcode = instruction.Opcode, Condition = instruction.Condition, Operand = instruction.Operand, Slot = slot };
|
||||
flattened.Add(value);
|
||||
buffer.Add(new Demo2RuleInstruction { Opcode = value.Opcode, Condition = value.Condition, Operand = value.Operand, Slot = value.Slot });
|
||||
}
|
||||
using var builder = new BlobBuilder(Allocator.Temp);
|
||||
ref var root = ref builder.ConstructRoot<Demo2RuleProgramBlob>();
|
||||
var blobArray = builder.Allocate(ref root.Instructions, flattened.Count);
|
||||
for (var i = 0; i < flattened.Count; i++) blobArray[i] = flattened[i];
|
||||
_ruleBlob = builder.CreateBlobAssetReference<Demo2RuleProgramBlob>(Allocator.Persistent);
|
||||
EntityManager.SetComponentData(Singleton, new Demo2RuleProgram { Value = _ruleBlob });
|
||||
}
|
||||
|
||||
public Demo2Snapshot CaptureSnapshot(IReadOnlyList<string>? ruleIds = null) => Demo2SnapshotCodec.Capture(EntityManager, Singleton, ruleIds);
|
||||
public void RestoreSnapshot(Demo2Snapshot snapshot) => Demo2SnapshotCodec.Restore(EntityManager, Singleton, snapshot);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_ruleBlob.IsCreated) _ruleBlob.Dispose();
|
||||
if (_world.IsCreated) _world.Dispose();
|
||||
}
|
||||
|
||||
private void Add<T>() where T : ComponentSystemBase, new() => _group.AddSystemToUpdateList(_world.CreateSystemManaged<T>());
|
||||
|
||||
private void CreateStarterLayout()
|
||||
{
|
||||
var state = EntityManager.GetComponentData<Demo2SimulationState>(Singleton);
|
||||
Demo2EcsFactory.CreateMachine(EntityManager, ++state.NextStableId, Demo2MachineKind.Miner, 3, 7, 0, 1);
|
||||
for (var x = 4; x <= 7; x++) Demo2EcsFactory.CreateMachine(EntityManager, ++state.NextStableId, Demo2MachineKind.Conveyor, x, 7, 0, 0);
|
||||
Demo2EcsFactory.CreateMachine(EntityManager, ++state.NextStableId, Demo2MachineKind.Smelter, 8, 7, 0, 3);
|
||||
for (var x = 9; x <= 11; x++) Demo2EcsFactory.CreateMachine(EntityManager, ++state.NextStableId, Demo2MachineKind.Conveyor, x, 7, 0, 0);
|
||||
Demo2EcsFactory.CreateMachine(EntityManager, ++state.NextStableId, Demo2MachineKind.Assembler, 12, 7, 0, 4);
|
||||
Demo2EcsFactory.CreateMachine(EntityManager, ++state.NextStableId, Demo2MachineKind.Loader, 15, 7, 0, 5);
|
||||
var turret = Demo2EcsFactory.CreateMachine(EntityManager, ++state.NextStableId, Demo2MachineKind.Turret, 20, 10, 0, 0);
|
||||
EntityManager.AddComponentData(turret, new Demo2TurretAmmo());
|
||||
EntityManager.SetComponentData(Singleton, state);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Demo2EcsFactory
|
||||
{
|
||||
public static Entity CreateMachine(EntityManager manager, int stableId, Demo2MachineKind kind, int x, int y, byte rotation, int recipeId)
|
||||
{
|
||||
var entity = manager.CreateEntity(typeof(Demo2StableId), typeof(Demo2GridPosition), typeof(Demo2MachineState), typeof(Demo2RecipeState));
|
||||
manager.SetName(entity, $"Machine {stableId} {kind}");
|
||||
manager.SetComponentData(entity, new Demo2StableId { Value = stableId });
|
||||
manager.SetComponentData(entity, new Demo2GridPosition { X = (short)x, Y = (short)y });
|
||||
var recipe = ResolveRecipe(recipeId == 0 ? DefaultRecipe(kind) : recipeId);
|
||||
manager.SetComponentData(entity, new Demo2MachineState
|
||||
{
|
||||
Kind = kind, Rotation = rotation, Enabled = 1, RecipeId = recipeId == 0 ? DefaultRecipe(kind) : recipeId,
|
||||
ProcessTicks = recipe.processTicks, Capacity = Capacity(kind)
|
||||
});
|
||||
manager.SetComponentData(entity, recipe.state);
|
||||
if (kind is Demo2MachineKind.Conveyor or Demo2MachineKind.Splitter)
|
||||
manager.AddComponentData(entity, new Demo2ConveyorState { Direction = rotation, Capacity = Capacity(kind) });
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static Entity CreateMaterial(EntityManager manager, int stableId, Demo2MaterialKind kind, int x, int y, byte direction, int quantity)
|
||||
{
|
||||
var entity = manager.CreateEntity(typeof(Demo2StableId), typeof(Demo2GridPosition), typeof(Demo2MaterialState));
|
||||
manager.SetName(entity, $"Material {stableId} {kind}");
|
||||
manager.SetComponentData(entity, new Demo2StableId { Value = stableId });
|
||||
manager.SetComponentData(entity, new Demo2GridPosition { X = (short)x, Y = (short)y });
|
||||
manager.SetComponentData(entity, new Demo2MaterialState { Kind = kind, Quantity = quantity, Direction = direction });
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static int DefaultRecipe(Demo2MachineKind kind) => kind switch
|
||||
{
|
||||
Demo2MachineKind.Miner => 1,
|
||||
Demo2MachineKind.Generator => 2,
|
||||
Demo2MachineKind.Smelter => 3,
|
||||
Demo2MachineKind.Assembler => 4,
|
||||
Demo2MachineKind.Loader => 5,
|
||||
Demo2MachineKind.NightShiftSmelter => 100,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
private static int Capacity(Demo2MachineKind kind) => kind switch
|
||||
{
|
||||
Demo2MachineKind.Conveyor => 4,
|
||||
Demo2MachineKind.Splitter => 8,
|
||||
Demo2MachineKind.Turret => 200,
|
||||
Demo2MachineKind.Loader => 24,
|
||||
Demo2MachineKind.NightShiftSmelter => 16,
|
||||
_ => 12
|
||||
};
|
||||
|
||||
private static (Demo2RecipeState state, int processTicks) ResolveRecipe(int id) => id switch
|
||||
{
|
||||
1 => (new Demo2RecipeState { Output = Demo2MaterialKind.IronOre, OutputCount = 1 }, 20),
|
||||
2 => (new Demo2RecipeState { Output = Demo2MaterialKind.Energy, OutputCount = 1 }, 30),
|
||||
3 => (new Demo2RecipeState { InputA = Demo2MaterialKind.IronOre, InputB = Demo2MaterialKind.Fuel, Output = Demo2MaterialKind.Ingot, InputCountA = 2, InputCountB = 1, OutputCount = 1 }, 40),
|
||||
4 => (new Demo2RecipeState { InputA = Demo2MaterialKind.Ingot, Output = Demo2MaterialKind.Gear, InputCountA = 2, OutputCount = 1 }, 50),
|
||||
5 => (new Demo2RecipeState { InputA = Demo2MaterialKind.Gear, InputB = Demo2MaterialKind.Energy, Output = Demo2MaterialKind.Ammo, InputCountA = 1, InputCountB = 1, OutputCount = 4 }, 35),
|
||||
100 => (new Demo2RecipeState { InputA = Demo2MaterialKind.IronOre, InputB = Demo2MaterialKind.Energy, Output = Demo2MaterialKind.Ingot, InputCountA = 2, InputCountB = 1, OutputCount = 2 }, 34),
|
||||
_ => (default, 1)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc27cb2d9904aa64fbdbbff14348aa1d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,145 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Demo2.Domain;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
namespace Demo2.ECS
|
||||
{
|
||||
public static class Demo2SnapshotCodec
|
||||
{
|
||||
public static Demo2Snapshot Capture(EntityManager manager, Entity singleton, IReadOnlyList<string>? ruleIds = null)
|
||||
{
|
||||
var simulation = manager.GetComponentData<Demo2SimulationState>(singleton);
|
||||
var economy = manager.GetComponentData<Demo2EconomyState>(singleton);
|
||||
var core = manager.GetComponentData<Demo2CoreHealth>(singleton);
|
||||
var stats = manager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
var snapshot = new Demo2Snapshot
|
||||
{
|
||||
Tick = simulation.Tick,
|
||||
Seed = simulation.Seed,
|
||||
Phase = simulation.Phase,
|
||||
Cycle = simulation.Cycle,
|
||||
CoreHealth = core.Value,
|
||||
Money = economy.Money,
|
||||
Payload = economy.Payload,
|
||||
MultiplierMilli = economy.MultiplierMilli,
|
||||
Energy = economy.Energy,
|
||||
ExcessFirepower = economy.ExcessFirepower,
|
||||
RuleAppliedCycle = simulation.RuleAppliedCycle,
|
||||
SpeedMultiplier = simulation.SpeedMultiplier,
|
||||
Extracted = stats.Extracted,
|
||||
Transported = stats.Transported,
|
||||
Processed = stats.Processed,
|
||||
AmmoProduced = stats.AmmoProduced,
|
||||
CompletedSameRecipeStreak = stats.CompletedSameRecipeStreak,
|
||||
LastRecipeId = stats.LastRecipeId,
|
||||
Faults = stats.Faults,
|
||||
BeltOccupancy = stats.BeltOccupancy,
|
||||
BeltCapacity = stats.BeltCapacity,
|
||||
IronFuelAdjacent = stats.IronFuelAdjacent,
|
||||
FirstBatch = stats.FirstBatch,
|
||||
DuplicateNextOutputs = stats.DuplicateNextOutputs,
|
||||
StateHash = simulation.StateHash,
|
||||
RuleIds = ruleIds == null ? new List<string>() : new List<string>(ruleIds)
|
||||
};
|
||||
|
||||
using var entities = manager.GetAllEntities(Allocator.Temp);
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
var entity = entities[i];
|
||||
if (!manager.HasComponent<Demo2StableId>(entity) || !manager.HasComponent<Demo2GridPosition>(entity)) continue;
|
||||
var row = new Demo2SnapshotEntity { StableId = manager.GetComponentData<Demo2StableId>(entity).Value };
|
||||
var position = manager.GetComponentData<Demo2GridPosition>(entity);
|
||||
row.X = position.X;
|
||||
row.Y = position.Y;
|
||||
if (manager.HasComponent<Demo2MachineState>(entity))
|
||||
{
|
||||
var machine = manager.GetComponentData<Demo2MachineState>(entity);
|
||||
row.MachineKind = machine.Kind;
|
||||
row.Rotation = machine.Rotation;
|
||||
row.RecipeId = machine.RecipeId;
|
||||
row.Progress = machine.ProgressTicks;
|
||||
row.Quantity = machine.StoredInput;
|
||||
row.Faults = machine.Faults;
|
||||
row.PendingOutputs = machine.PendingOutputs;
|
||||
if (manager.HasComponent<Demo2TurretAmmo>(entity)) row.TurretAmmo = manager.GetComponentData<Demo2TurretAmmo>(entity).Value;
|
||||
}
|
||||
else if (manager.HasComponent<Demo2MaterialState>(entity))
|
||||
{
|
||||
var material = manager.GetComponentData<Demo2MaterialState>(entity);
|
||||
row.MaterialKind = material.Kind;
|
||||
row.Rotation = material.Direction;
|
||||
row.Progress = material.SubCellProgress;
|
||||
row.Quantity = material.Quantity;
|
||||
row.MachineKind = (Demo2MachineKind)255;
|
||||
}
|
||||
snapshot.Entities.Add(row);
|
||||
}
|
||||
snapshot.Entities.Sort((left, right) => left.StableId.CompareTo(right.StableId));
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public static void Restore(EntityManager manager, Entity singleton, Demo2Snapshot snapshot)
|
||||
{
|
||||
using (var all = manager.GetAllEntities(Allocator.Temp))
|
||||
for (var i = 0; i < all.Length; i++) if (all[i] != singleton) manager.DestroyEntity(all[i]);
|
||||
|
||||
var simulation = manager.GetComponentData<Demo2SimulationState>(singleton);
|
||||
simulation.Tick = snapshot.Tick;
|
||||
simulation.Seed = snapshot.Seed;
|
||||
simulation.Phase = snapshot.Phase;
|
||||
simulation.Cycle = snapshot.Cycle;
|
||||
simulation.StateHash = snapshot.StateHash;
|
||||
simulation.RuleAppliedCycle = snapshot.RuleAppliedCycle;
|
||||
simulation.SpeedMultiplier = snapshot.SpeedMultiplier;
|
||||
simulation.NextStableId = 0;
|
||||
manager.SetComponentData(singleton, simulation);
|
||||
manager.SetComponentData(singleton, new Demo2CoreHealth { Value = snapshot.CoreHealth });
|
||||
manager.SetComponentData(singleton, new Demo2EconomyState
|
||||
{
|
||||
Money = snapshot.Money, Payload = snapshot.Payload, MultiplierMilli = snapshot.MultiplierMilli,
|
||||
Energy = snapshot.Energy,
|
||||
ExcessFirepower = snapshot.ExcessFirepower
|
||||
});
|
||||
manager.SetComponentData(singleton, new Demo2CycleStats
|
||||
{
|
||||
Extracted = snapshot.Extracted,
|
||||
Transported = snapshot.Transported,
|
||||
Processed = snapshot.Processed,
|
||||
AmmoProduced = snapshot.AmmoProduced,
|
||||
CompletedSameRecipeStreak = snapshot.CompletedSameRecipeStreak,
|
||||
LastRecipeId = snapshot.LastRecipeId,
|
||||
Faults = snapshot.Faults,
|
||||
BeltOccupancy = snapshot.BeltOccupancy,
|
||||
BeltCapacity = snapshot.BeltCapacity,
|
||||
IronFuelAdjacent = snapshot.IronFuelAdjacent,
|
||||
FirstBatch = snapshot.FirstBatch,
|
||||
DuplicateNextOutputs = snapshot.DuplicateNextOutputs
|
||||
});
|
||||
|
||||
foreach (var row in snapshot.Entities)
|
||||
{
|
||||
if ((byte)row.MachineKind == 255)
|
||||
Demo2EcsFactory.CreateMaterial(manager, row.StableId, row.MaterialKind, row.X, row.Y, row.Rotation, row.Quantity);
|
||||
else
|
||||
{
|
||||
var entity = Demo2EcsFactory.CreateMachine(manager, row.StableId, row.MachineKind, row.X, row.Y, row.Rotation, row.RecipeId);
|
||||
var machine = manager.GetComponentData<Demo2MachineState>(entity);
|
||||
machine.ProgressTicks = row.Progress;
|
||||
machine.StoredInput = row.Quantity;
|
||||
machine.Faults = row.Faults;
|
||||
machine.PendingOutputs = row.PendingOutputs;
|
||||
manager.SetComponentData(entity, machine);
|
||||
if (row.MachineKind == Demo2MachineKind.Turret && !manager.HasComponent<Demo2TurretAmmo>(entity))
|
||||
manager.AddComponentData(entity, new Demo2TurretAmmo());
|
||||
if (manager.HasComponent<Demo2TurretAmmo>(entity))
|
||||
manager.SetComponentData(entity, new Demo2TurretAmmo { Value = row.TurretAmmo });
|
||||
}
|
||||
if (row.StableId > simulation.NextStableId) simulation.NextStableId = row.StableId;
|
||||
}
|
||||
manager.SetComponentData(singleton, simulation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46554d8358fcb914bbe6b75006e83b57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,538 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using Demo2.Domain;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
namespace Demo2.ECS
|
||||
{
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2SimulationSystemGroup : ComponentSystemGroup { }
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2BuildCommandSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var singletonQuery = GetEntityQuery(typeof(Demo2SimulationState), typeof(Demo2EconomyState), typeof(Demo2BuildCommandElement), typeof(Demo2ProcessedCommandToken));
|
||||
if (singletonQuery.IsEmptyIgnoreFilter) return;
|
||||
var singleton = singletonQuery.GetSingletonEntity();
|
||||
var simulation = EntityManager.GetComponentData<Demo2SimulationState>(singleton);
|
||||
if (simulation.Phase != Demo2Phase.Build) return;
|
||||
|
||||
var economy = EntityManager.GetComponentData<Demo2EconomyState>(singleton);
|
||||
var commands = EntityManager.GetBuffer<Demo2BuildCommandElement>(singleton);
|
||||
var tokens = EntityManager.GetBuffer<Demo2ProcessedCommandToken>(singleton);
|
||||
var pending = commands.ToNativeArray(Allocator.Temp);
|
||||
var knownTokens = new NativeArray<FixedString64Bytes>(tokens.Length + pending.Length, Allocator.Temp);
|
||||
var knownCount = tokens.Length;
|
||||
for (var i = 0; i < tokens.Length; i++) knownTokens[i] = tokens[i].Value;
|
||||
commands.Clear();
|
||||
for (var index = 0; index < pending.Length; index++)
|
||||
{
|
||||
var command = pending[index];
|
||||
if (Contains(knownTokens, knownCount, command.IdempotencyToken)) continue;
|
||||
knownTokens[knownCount++] = command.IdempotencyToken;
|
||||
if (command.X < 0 || command.X >= Demo2Protocol.GridWidth || command.Y < 0 || command.Y >= Demo2Protocol.GridHeight) continue;
|
||||
|
||||
var existing = FindMachine(command.X, command.Y);
|
||||
if (command.Remove != 0)
|
||||
{
|
||||
if (existing != Entity.Null)
|
||||
{
|
||||
var machine = EntityManager.GetComponentData<Demo2MachineState>(existing);
|
||||
economy.Money = SaturatingMath.Add(economy.Money, Cost(machine.Kind));
|
||||
EntityManager.DestroyEntity(existing);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var cost = Cost(command.Kind);
|
||||
if (existing != Entity.Null || economy.Money < cost) continue;
|
||||
economy.Money -= cost;
|
||||
simulation.NextStableId++;
|
||||
Demo2EcsFactory.CreateMachine(EntityManager, simulation.NextStableId, command.Kind, command.X, command.Y, command.Rotation, command.RecipeId);
|
||||
}
|
||||
tokens = EntityManager.GetBuffer<Demo2ProcessedCommandToken>(singleton);
|
||||
tokens.Clear();
|
||||
for (var i = Math.Max(0, knownCount - 256); i < knownCount; i++)
|
||||
tokens.Add(new Demo2ProcessedCommandToken { Value = knownTokens[i] });
|
||||
pending.Dispose();
|
||||
knownTokens.Dispose();
|
||||
EntityManager.SetComponentData(singleton, simulation);
|
||||
EntityManager.SetComponentData(singleton, economy);
|
||||
}
|
||||
|
||||
private Entity FindMachine(int x, int y)
|
||||
{
|
||||
var query = GetEntityQuery(typeof(Demo2GridPosition), typeof(Demo2MachineState));
|
||||
using var entities = query.ToEntityArray(Allocator.Temp);
|
||||
using var positions = query.ToComponentDataArray<Demo2GridPosition>(Allocator.Temp);
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
if (positions[i].X == x && positions[i].Y == y) return entities[i];
|
||||
return Entity.Null;
|
||||
}
|
||||
|
||||
private static bool Contains(NativeArray<FixedString64Bytes> tokens, int count, FixedString64Bytes token)
|
||||
{
|
||||
for (var i = 0; i < count; i++) if (tokens[i].Equals(token)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int Cost(Demo2MachineKind kind) => kind switch
|
||||
{
|
||||
Demo2MachineKind.Miner => 40,
|
||||
Demo2MachineKind.Conveyor => 5,
|
||||
Demo2MachineKind.Splitter => 15,
|
||||
Demo2MachineKind.Smelter => 80,
|
||||
Demo2MachineKind.Assembler => 110,
|
||||
Demo2MachineKind.Loader => 70,
|
||||
Demo2MachineKind.Turret => 150,
|
||||
Demo2MachineKind.Generator => 60,
|
||||
Demo2MachineKind.NightShiftSmelter => 95,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2ExtractionSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (!Demo2SystemUtility.TryGetProductionState(EntityManager, out var singleton, out var simulation)) return;
|
||||
var stats = EntityManager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
var query = GetEntityQuery(typeof(Demo2StableId), typeof(Demo2GridPosition), typeof(Demo2MachineState), typeof(Demo2RecipeState));
|
||||
using var entities = query.ToEntityArray(Allocator.Temp);
|
||||
using var positions = query.ToComponentDataArray<Demo2GridPosition>(Allocator.Temp);
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
var machine = EntityManager.GetComponentData<Demo2MachineState>(entities[i]);
|
||||
if (machine.Enabled == 0 || (machine.Kind != Demo2MachineKind.Miner && machine.Kind != Demo2MachineKind.Generator)) continue;
|
||||
machine.ProgressTicks++;
|
||||
if (machine.ProgressTicks < Math.Max(1, machine.ProcessTicks))
|
||||
{
|
||||
EntityManager.SetComponentData(entities[i], machine);
|
||||
continue;
|
||||
}
|
||||
machine.ProgressTicks = 0;
|
||||
var recipe = EntityManager.GetComponentData<Demo2RecipeState>(entities[i]);
|
||||
simulation.NextStableId++;
|
||||
Demo2EcsFactory.CreateMaterial(EntityManager, simulation.NextStableId, recipe.Output, positions[i].X, positions[i].Y, machine.Rotation, Math.Max(1, recipe.OutputCount));
|
||||
stats.Extracted = SaturatingMath.Add(stats.Extracted, recipe.OutputCount);
|
||||
if (recipe.Output == Demo2MaterialKind.Energy)
|
||||
{
|
||||
var economy = EntityManager.GetComponentData<Demo2EconomyState>(singleton);
|
||||
economy.Energy = SaturatingMath.Add(economy.Energy, recipe.OutputCount);
|
||||
EntityManager.SetComponentData(singleton, economy);
|
||||
}
|
||||
EntityManager.SetComponentData(entities[i], machine);
|
||||
}
|
||||
EntityManager.SetComponentData(singleton, simulation);
|
||||
EntityManager.SetComponentData(singleton, stats);
|
||||
}
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2ConveyorSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (!Demo2SystemUtility.TryGetProductionState(EntityManager, out var singleton, out _)) return;
|
||||
var stats = EntityManager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
stats.BeltOccupancy = 0;
|
||||
stats.BeltCapacity = 0;
|
||||
var beltQuery = GetEntityQuery(typeof(Demo2ConveyorState));
|
||||
using (var belts = beltQuery.ToComponentDataArray<Demo2ConveyorState>(Allocator.Temp))
|
||||
for (var i = 0; i < belts.Length; i++) stats.BeltCapacity += Math.Max(1, belts[i].Capacity);
|
||||
|
||||
var query = GetEntityQuery(typeof(Demo2GridPosition), typeof(Demo2MaterialState));
|
||||
using var entities = query.ToEntityArray(Allocator.Temp);
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
var position = EntityManager.GetComponentData<Demo2GridPosition>(entities[i]);
|
||||
var material = EntityManager.GetComponentData<Demo2MaterialState>(entities[i]);
|
||||
material.SubCellProgress += 250;
|
||||
stats.BeltOccupancy += material.Quantity;
|
||||
if (material.SubCellProgress >= 1000)
|
||||
{
|
||||
material.SubCellProgress -= 1000;
|
||||
Demo2SystemUtility.Move(ref position, material.Direction);
|
||||
stats.Transported = SaturatingMath.Add(stats.Transported, material.Quantity);
|
||||
}
|
||||
EntityManager.SetComponentData(entities[i], position);
|
||||
EntityManager.SetComponentData(entities[i], material);
|
||||
}
|
||||
EntityManager.SetComponentData(singleton, stats);
|
||||
}
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2InputAllocationSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (!Demo2SystemUtility.TryGetProductionState(EntityManager, out var singleton, out _)) return;
|
||||
var stats = EntityManager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
var materialQuery = GetEntityQuery(typeof(Demo2GridPosition), typeof(Demo2MaterialState));
|
||||
var machineQuery = GetEntityQuery(typeof(Demo2GridPosition), typeof(Demo2MachineState));
|
||||
using var materials = materialQuery.ToEntityArray(Allocator.Temp);
|
||||
using var materialPositions = materialQuery.ToComponentDataArray<Demo2GridPosition>(Allocator.Temp);
|
||||
using var materialStates = materialQuery.ToComponentDataArray<Demo2MaterialState>(Allocator.Temp);
|
||||
using var machines = machineQuery.ToEntityArray(Allocator.Temp);
|
||||
using var machinePositions = machineQuery.ToComponentDataArray<Demo2GridPosition>(Allocator.Temp);
|
||||
for (var m = 0; m < materials.Length; m++)
|
||||
{
|
||||
if (!EntityManager.Exists(materials[m])) continue;
|
||||
for (var i = 0; i < machines.Length; i++)
|
||||
{
|
||||
if (materialPositions[m].X != machinePositions[i].X || materialPositions[m].Y != machinePositions[i].Y) continue;
|
||||
var machine = EntityManager.GetComponentData<Demo2MachineState>(machines[i]);
|
||||
if (machine.Kind is Demo2MachineKind.Conveyor or Demo2MachineKind.Splitter or Demo2MachineKind.Miner or Demo2MachineKind.Generator or Demo2MachineKind.Turret) continue;
|
||||
if (machine.StoredInput + materialStates[m].Quantity > machine.Capacity) continue;
|
||||
machine.StoredInput += materialStates[m].Quantity;
|
||||
EntityManager.SetComponentData(machines[i], machine);
|
||||
EntityManager.DestroyEntity(materials[m]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stats.IronFuelAdjacent = HasAdjacentMaterial(materialPositions, materialStates, Demo2MaterialKind.IronOre, Demo2MaterialKind.Fuel) ? (byte)1 : (byte)0;
|
||||
EntityManager.SetComponentData(singleton, stats);
|
||||
}
|
||||
|
||||
private static bool HasAdjacentMaterial(NativeArray<Demo2GridPosition> positions, NativeArray<Demo2MaterialState> materials, Demo2MaterialKind a, Demo2MaterialKind b)
|
||||
{
|
||||
for (var i = 0; i < positions.Length; i++)
|
||||
for (var j = i + 1; j < positions.Length; j++)
|
||||
if (((materials[i].Kind == a && materials[j].Kind == b) || (materials[i].Kind == b && materials[j].Kind == a)) &&
|
||||
Math.Abs(positions[i].X - positions[j].X) + Math.Abs(positions[i].Y - positions[j].Y) == 1) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2ProcessingSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (!Demo2SystemUtility.TryGetProductionState(EntityManager, out var singleton, out _)) return;
|
||||
var stats = EntityManager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
stats.Faults = 0;
|
||||
var query = GetEntityQuery(typeof(Demo2MachineState), typeof(Demo2RecipeState));
|
||||
using var entities = query.ToEntityArray(Allocator.Temp);
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
var machine = EntityManager.GetComponentData<Demo2MachineState>(entities[i]);
|
||||
if (machine.Kind is Demo2MachineKind.Miner or Demo2MachineKind.Generator or Demo2MachineKind.Conveyor or Demo2MachineKind.Splitter or Demo2MachineKind.Turret) continue;
|
||||
var recipe = EntityManager.GetComponentData<Demo2RecipeState>(entities[i]);
|
||||
var needed = Math.Max(1, recipe.InputCountA + recipe.InputCountB);
|
||||
if (machine.StoredInput < needed) continue;
|
||||
machine.ProgressTicks++;
|
||||
if (machine.ProgressTicks >= Math.Max(1, machine.ProcessTicks))
|
||||
{
|
||||
machine.ProgressTicks = 0;
|
||||
machine.StoredInput -= needed;
|
||||
machine.PendingOutputs += Math.Max(1, recipe.OutputCount);
|
||||
stats.Processed = SaturatingMath.Add(stats.Processed, 1);
|
||||
stats.FirstBatch = stats.Processed == 1 ? (byte)1 : stats.FirstBatch;
|
||||
if (stats.LastRecipeId == machine.RecipeId) stats.CompletedSameRecipeStreak++;
|
||||
else { stats.LastRecipeId = machine.RecipeId; stats.CompletedSameRecipeStreak = 1; }
|
||||
}
|
||||
stats.Faults += machine.Faults;
|
||||
EntityManager.SetComponentData(entities[i], machine);
|
||||
}
|
||||
EntityManager.SetComponentData(singleton, stats);
|
||||
}
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2OutputSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (!Demo2SystemUtility.TryGetProductionState(EntityManager, out var singleton, out var simulation)) return;
|
||||
var stats = EntityManager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
var query = GetEntityQuery(typeof(Demo2GridPosition), typeof(Demo2MachineState), typeof(Demo2RecipeState));
|
||||
using var entities = query.ToEntityArray(Allocator.Temp);
|
||||
using var positions = query.ToComponentDataArray<Demo2GridPosition>(Allocator.Temp);
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
var machine = EntityManager.GetComponentData<Demo2MachineState>(entities[i]);
|
||||
if (machine.PendingOutputs <= 0) continue;
|
||||
var recipe = EntityManager.GetComponentData<Demo2RecipeState>(entities[i]);
|
||||
var quantity = machine.PendingOutputs;
|
||||
if (stats.DuplicateNextOutputs > 0) { quantity *= 2; stats.DuplicateNextOutputs--; }
|
||||
simulation.NextStableId++;
|
||||
Demo2EcsFactory.CreateMaterial(EntityManager, simulation.NextStableId, recipe.Output, positions[i].X, positions[i].Y, machine.Rotation, quantity);
|
||||
if (recipe.Output == Demo2MaterialKind.Ammo) stats.AmmoProduced = SaturatingMath.Add(stats.AmmoProduced, quantity);
|
||||
machine.PendingOutputs = 0;
|
||||
EntityManager.SetComponentData(entities[i], machine);
|
||||
}
|
||||
EntityManager.SetComponentData(singleton, simulation);
|
||||
EntityManager.SetComponentData(singleton, stats);
|
||||
}
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2RuleTriggerSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var query = GetEntityQuery(typeof(Demo2SimulationState), typeof(Demo2EconomyState), typeof(Demo2CycleStats), typeof(Demo2RuleInstruction));
|
||||
if (query.IsEmptyIgnoreFilter) return;
|
||||
var singleton = query.GetSingletonEntity();
|
||||
var simulation = EntityManager.GetComponentData<Demo2SimulationState>(singleton);
|
||||
if (simulation.Phase != Demo2Phase.Defense || simulation.RuleAppliedCycle == simulation.Cycle) return;
|
||||
var economy = EntityManager.GetComponentData<Demo2EconomyState>(singleton);
|
||||
var stats = EntityManager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
var context = new Demo2RuleContext
|
||||
{
|
||||
Payload = economy.Payload,
|
||||
MultiplierMilli = economy.MultiplierMilli,
|
||||
Energy = economy.Energy,
|
||||
Money = economy.Money,
|
||||
ExcessFirepower = economy.ExcessFirepower,
|
||||
Faults = stats.Faults,
|
||||
ThreeSameRecipes = stats.CompletedSameRecipeStreak >= 3,
|
||||
IronFuelAdjacent = stats.IronFuelAdjacent != 0,
|
||||
BeltBelowEightyPercent = stats.BeltCapacity <= 0 || stats.BeltOccupancy * 100 < stats.BeltCapacity * 80,
|
||||
FirstBatch = stats.FirstBatch != 0,
|
||||
CoreAtRisk = EntityManager.GetComponentData<Demo2CoreHealth>(singleton).Value <= 1,
|
||||
DuplicatedOutputs = stats.DuplicateNextOutputs
|
||||
};
|
||||
var instructions = EntityManager.GetBuffer<Demo2RuleInstruction>(singleton);
|
||||
for (var i = 0; i < instructions.Length; i++)
|
||||
{
|
||||
var instruction = instructions[i];
|
||||
var data = new Demo2RuleInstructionData { Opcode = instruction.Opcode, Condition = instruction.Condition, Operand = instruction.Operand };
|
||||
if (Demo2RuleEngine.Matches(data.Condition, context)) Demo2RuleEngine.Apply(ref context, data);
|
||||
}
|
||||
economy.Payload = context.Payload;
|
||||
economy.MultiplierMilli = context.MultiplierMilli;
|
||||
economy.Energy = context.Energy;
|
||||
economy.Money = context.Money;
|
||||
stats.Faults = context.Faults;
|
||||
stats.DuplicateNextOutputs = context.DuplicatedOutputs;
|
||||
simulation.RuleAppliedCycle = simulation.Cycle;
|
||||
EntityManager.SetComponentData(singleton, simulation);
|
||||
EntityManager.SetComponentData(singleton, economy);
|
||||
EntityManager.SetComponentData(singleton, stats);
|
||||
}
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2TurretLoadingSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
if (!Demo2SystemUtility.TryGetProductionState(EntityManager, out _, out _)) return;
|
||||
var materialQuery = GetEntityQuery(typeof(Demo2MaterialState));
|
||||
var turretQuery = GetEntityQuery(typeof(Demo2TurretAmmo), typeof(Demo2StableId));
|
||||
if (turretQuery.IsEmptyIgnoreFilter) return;
|
||||
using var turretEntities = turretQuery.ToEntityArray(Allocator.Temp);
|
||||
using var turretIds = turretQuery.ToComponentDataArray<Demo2StableId>(Allocator.Temp);
|
||||
var turret = turretEntities[0];
|
||||
var lowestId = turretIds[0].Value;
|
||||
for (var i = 1; i < turretEntities.Length; i++)
|
||||
if (turretIds[i].Value < lowestId)
|
||||
{
|
||||
turret = turretEntities[i];
|
||||
lowestId = turretIds[i].Value;
|
||||
}
|
||||
var ammo = EntityManager.GetComponentData<Demo2TurretAmmo>(turret);
|
||||
using var entities = materialQuery.ToEntityArray(Allocator.Temp);
|
||||
using var materials = materialQuery.ToComponentDataArray<Demo2MaterialState>(Allocator.Temp);
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
if (materials[i].Kind != Demo2MaterialKind.Ammo || !EntityManager.Exists(entities[i])) continue;
|
||||
ammo.Value = SaturatingMath.Add(ammo.Value, materials[i].Quantity);
|
||||
EntityManager.DestroyEntity(entities[i]);
|
||||
}
|
||||
EntityManager.SetComponentData(turret, ammo);
|
||||
}
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2CycleStatisticsSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var query = GetEntityQuery(typeof(Demo2SimulationState), typeof(Demo2EconomyState), typeof(Demo2CycleStats));
|
||||
if (query.IsEmptyIgnoreFilter) return;
|
||||
var singleton = query.GetSingletonEntity();
|
||||
var simulation = EntityManager.GetComponentData<Demo2SimulationState>(singleton);
|
||||
if (simulation.Phase != Demo2Phase.Production) return;
|
||||
simulation.Tick++;
|
||||
var stats = EntityManager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
var economy = EntityManager.GetComponentData<Demo2EconomyState>(singleton);
|
||||
economy.Payload = SaturatingMath.Add(economy.Payload, stats.AmmoProduced);
|
||||
stats.AmmoProduced = 0;
|
||||
EntityManager.SetComponentData(singleton, simulation);
|
||||
EntityManager.SetComponentData(singleton, stats);
|
||||
EntityManager.SetComponentData(singleton, economy);
|
||||
}
|
||||
}
|
||||
|
||||
[DisableAutoCreation]
|
||||
public sealed partial class Demo2StateHashSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var singletonQuery = GetEntityQuery(typeof(Demo2SimulationState), typeof(Demo2EconomyState), typeof(Demo2CoreHealth));
|
||||
if (singletonQuery.IsEmptyIgnoreFilter) return;
|
||||
var singleton = singletonQuery.GetSingletonEntity();
|
||||
var simulation = EntityManager.GetComponentData<Demo2SimulationState>(singleton);
|
||||
var economy = EntityManager.GetComponentData<Demo2EconomyState>(singleton);
|
||||
var core = EntityManager.GetComponentData<Demo2CoreHealth>(singleton);
|
||||
var stats = EntityManager.GetComponentData<Demo2CycleStats>(singleton);
|
||||
var hash = Demo2Hash.Offset;
|
||||
Demo2Hash.Add(ref hash, simulation.Tick);
|
||||
Demo2Hash.Add(ref hash, simulation.Seed);
|
||||
Demo2Hash.Add(ref hash, (int)simulation.Phase);
|
||||
Demo2Hash.Add(ref hash, simulation.Cycle);
|
||||
Demo2Hash.Add(ref hash, simulation.RuleAppliedCycle);
|
||||
Demo2Hash.Add(ref hash, simulation.SpeedMultiplier);
|
||||
Demo2Hash.Add(ref hash, economy.Money);
|
||||
Demo2Hash.Add(ref hash, economy.Payload);
|
||||
Demo2Hash.Add(ref hash, economy.MultiplierMilli);
|
||||
Demo2Hash.Add(ref hash, economy.Energy);
|
||||
Demo2Hash.Add(ref hash, economy.ExcessFirepower);
|
||||
Demo2Hash.Add(ref hash, core.Value);
|
||||
Demo2Hash.Add(ref hash, stats.Extracted);
|
||||
Demo2Hash.Add(ref hash, stats.Transported);
|
||||
Demo2Hash.Add(ref hash, stats.Processed);
|
||||
Demo2Hash.Add(ref hash, stats.AmmoProduced);
|
||||
Demo2Hash.Add(ref hash, stats.CompletedSameRecipeStreak);
|
||||
Demo2Hash.Add(ref hash, stats.LastRecipeId);
|
||||
Demo2Hash.Add(ref hash, stats.Faults);
|
||||
Demo2Hash.Add(ref hash, stats.BeltOccupancy);
|
||||
Demo2Hash.Add(ref hash, stats.BeltCapacity);
|
||||
Demo2Hash.Add(ref hash, stats.IronFuelAdjacent);
|
||||
Demo2Hash.Add(ref hash, stats.FirstBatch);
|
||||
Demo2Hash.Add(ref hash, stats.DuplicateNextOutputs);
|
||||
|
||||
var query = GetEntityQuery(typeof(Demo2StableId));
|
||||
using var entities = query.ToEntityArray(Allocator.Temp);
|
||||
using var ids = query.ToComponentDataArray<Demo2StableId>(Allocator.Temp);
|
||||
var rows = new NativeArray<Demo2HashRow>(entities.Length, Allocator.Temp);
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
var row = new Demo2HashRow { StableId = ids[i].Value };
|
||||
if (EntityManager.HasComponent<Demo2GridPosition>(entities[i]))
|
||||
{
|
||||
var position = EntityManager.GetComponentData<Demo2GridPosition>(entities[i]);
|
||||
row.X = position.X;
|
||||
row.Y = position.Y;
|
||||
}
|
||||
if (EntityManager.HasComponent<Demo2MachineState>(entities[i]))
|
||||
{
|
||||
var machine = EntityManager.GetComponentData<Demo2MachineState>(entities[i]);
|
||||
row.Kind = (int)machine.Kind;
|
||||
row.Rotation = machine.Rotation;
|
||||
row.Enabled = machine.Enabled;
|
||||
row.RecipeId = machine.RecipeId;
|
||||
row.StoredInput = machine.StoredInput;
|
||||
row.ProgressTicks = machine.ProgressTicks;
|
||||
row.ProcessTicks = machine.ProcessTicks;
|
||||
row.Capacity = machine.Capacity;
|
||||
row.PendingOutputs = machine.PendingOutputs;
|
||||
row.Faults = machine.Faults;
|
||||
if (EntityManager.HasComponent<Demo2TurretAmmo>(entities[i]))
|
||||
row.TurretAmmo = EntityManager.GetComponentData<Demo2TurretAmmo>(entities[i]).Value;
|
||||
}
|
||||
if (EntityManager.HasComponent<Demo2MaterialState>(entities[i]))
|
||||
{
|
||||
var material = EntityManager.GetComponentData<Demo2MaterialState>(entities[i]);
|
||||
row.Kind = (int)material.Kind;
|
||||
row.Quantity = material.Quantity;
|
||||
row.SubCellProgress = material.SubCellProgress;
|
||||
row.Rotation = material.Direction;
|
||||
}
|
||||
rows[i] = row;
|
||||
}
|
||||
rows.Sort();
|
||||
for (var i = 0; i < rows.Length; i++)
|
||||
{
|
||||
var row = rows[i];
|
||||
Demo2Hash.Add(ref hash, row.StableId);
|
||||
Demo2Hash.Add(ref hash, row.X);
|
||||
Demo2Hash.Add(ref hash, row.Y);
|
||||
Demo2Hash.Add(ref hash, row.Kind);
|
||||
Demo2Hash.Add(ref hash, row.Rotation);
|
||||
Demo2Hash.Add(ref hash, row.Enabled);
|
||||
Demo2Hash.Add(ref hash, row.RecipeId);
|
||||
Demo2Hash.Add(ref hash, row.StoredInput);
|
||||
Demo2Hash.Add(ref hash, row.ProgressTicks);
|
||||
Demo2Hash.Add(ref hash, row.ProcessTicks);
|
||||
Demo2Hash.Add(ref hash, row.Capacity);
|
||||
Demo2Hash.Add(ref hash, row.PendingOutputs);
|
||||
Demo2Hash.Add(ref hash, row.Faults);
|
||||
Demo2Hash.Add(ref hash, row.Quantity);
|
||||
Demo2Hash.Add(ref hash, row.SubCellProgress);
|
||||
Demo2Hash.Add(ref hash, row.TurretAmmo);
|
||||
}
|
||||
rows.Dispose();
|
||||
simulation.StateHash = hash;
|
||||
EntityManager.SetComponentData(singleton, simulation);
|
||||
}
|
||||
|
||||
private struct Demo2HashRow : IComparable<Demo2HashRow>
|
||||
{
|
||||
public int StableId;
|
||||
public int X;
|
||||
public int Y;
|
||||
public int Kind;
|
||||
public int Rotation;
|
||||
public int Enabled;
|
||||
public int RecipeId;
|
||||
public int StoredInput;
|
||||
public int ProgressTicks;
|
||||
public int ProcessTicks;
|
||||
public int Capacity;
|
||||
public int PendingOutputs;
|
||||
public int Faults;
|
||||
public int Quantity;
|
||||
public int SubCellProgress;
|
||||
public long TurretAmmo;
|
||||
|
||||
public int CompareTo(Demo2HashRow other) => StableId.CompareTo(other.StableId);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Demo2SystemUtility
|
||||
{
|
||||
public static bool TryGetProductionState(EntityManager manager, out Entity singleton, out Demo2SimulationState simulation)
|
||||
{
|
||||
var query = manager.CreateEntityQuery(typeof(Demo2SimulationState));
|
||||
if (query.IsEmptyIgnoreFilter) { singleton = Entity.Null; simulation = default; return false; }
|
||||
singleton = query.GetSingletonEntity();
|
||||
simulation = manager.GetComponentData<Demo2SimulationState>(singleton);
|
||||
return simulation.Phase == Demo2Phase.Production;
|
||||
}
|
||||
|
||||
public static void Move(ref Demo2GridPosition position, byte direction)
|
||||
{
|
||||
switch (direction & 3)
|
||||
{
|
||||
case 0: position.X++; break;
|
||||
case 1: position.Y--; break;
|
||||
case 2: position.X--; break;
|
||||
case 3: position.Y++; break;
|
||||
}
|
||||
if (position.X < 0) position.X = 0;
|
||||
if (position.X >= Demo2Protocol.GridWidth) position.X = Demo2Protocol.GridWidth - 1;
|
||||
if (position.Y < 0) position.Y = 0;
|
||||
if (position.Y >= Demo2Protocol.GridHeight) position.Y = Demo2Protocol.GridHeight - 1;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Demo2Hash
|
||||
{
|
||||
public const ulong Offset = 14695981039346656037UL;
|
||||
private const ulong Prime = 1099511628211UL;
|
||||
public static void Add(ref ulong hash, long value) { unchecked { hash ^= (ulong)value; hash *= Prime; } }
|
||||
public static void Add(ref ulong hash, ulong value) { unchecked { hash ^= value; hash *= Prime; } }
|
||||
public static void Add(ref ulong hash, int value) => Add(ref hash, (long)value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5d69845118ea8f458a29670df8f6bb6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user