Skip to content

Game.City.DevTreeSystem

Assembly:
Namespace: Game.City

Type: class

Base: GameSystemBase

Summary:
DevTreeSystem manages the player's Development Tree points and purchases in Cities: Skylines 2. It: - Listens for milestone reach events and accumulates dev-tree points (via a Burst-compiled job). - Exposes a points property for reading/writing the current DevTreePoints singleton. - Handles purchasing of Dev Tree nodes (by Prefab or Entity), validating cost, service availability and node requirements, and issuing Unlock events through an EndFrameBarrier command buffer. - Uses Unity ECS queries and component lookups to operate on MilestoneReachedEvent, DevTreePoints and DevTreeNode related data efficiently.


Fields

  • private EndFrameBarrier m_EndFrameBarrier
    This system's reference to the EndFrameBarrier system used to create an EntityCommandBuffer for issuing Unlock events at the end of the frame.

  • private EntityQuery m_MilestoneReachedQuery
    EntityQuery to find entities with MilestoneReachedEvent components. Used to collect milestone events to convert into dev-tree points.

  • private EntityQuery m_DevTreePointsQuery
    EntityQuery for the singleton DevTreePoints component. This is required for the system to run and is used to read/set the player's current dev-tree points.

  • private EntityArchetype m_UnlockEventArchetype
    Archetype for creating Unlock event entities (components: Unlock, Event). Used when a node is successfully purchased.

  • private PrefabSystem m_PrefabSystem
    Cached reference to the PrefabSystem used to map node prefabs <-> entities and to emit telemetry with the prefab information.

  • private TypeHandle __TypeHandle
    Holds ComponentLookup/ComponentTypeHandle instances used by the system for efficient access to component data (MilestoneData, DevTreePoints, DevTreeNodeData, Locked). Populated in OnCreateForCompiler.

Properties

  • public int points { get; set; }
    Gets or sets the current DevTreePoints.m_Points value from the DevTreePoints singleton (if present). Reading returns 0 if the DevTreePoints singleton is missing. Writing will set the singleton's m_Points value. This is the main API for querying/modifying available dev-tree points at runtime.

Constructors

  • public DevTreeSystem()
    Default constructor. Component/system initialization is performed in OnCreate / OnCreateForCompiler.

Methods

  • protected override void OnCreate() : System.Void
    Sets up entity queries (MilestoneReachedEvent and DevTreePoints), creates the Unlock event archetype, resolves PrefabSystem and EndFrameBarrier from the world, and marks DevTreePoints query as required for system update (RequireForUpdate). This prepares the system to react to milestone events and perform purchases.

  • protected override void OnUpdate() : System.Void
    If there are any MilestoneReachedEvent entities, schedules a Burst-compiled AppendPointsJob to:

  • Read the DevTreePoints singleton chunk,
  • Read all MilestoneReachedEvent entries,
  • For each reached milestone, add the milestone's configured dev-tree points (or a default based on milestone index if no MilestoneData exists). The job uses asynchronous chunk/list creation (ToArchetypeChunkListAsync / ToComponentDataListAsync) and disposes the temporary NativeLists with the job dependency. The system's Dependency is updated to include the scheduled job.

  • public void Purchase(DevTreeNodePrefab nodePrefab) : System.Void
    Convenience overload that resolves a DevTreeNodePrefab to its Entity via PrefabSystem and calls Purchase(Entity). Only executes if the DevTreePoints singleton exists.

  • public void Purchase(Entity node) : System.Void
    Attempts to purchase the specified dev-tree node entity. The method:

  • Looks up the DevTreeNodeData and Locked components for the node.
  • Checks if the player has enough points and the node/service are in a locked/enabled state appropriate for purchase.
  • Checks node requirements (DevTreeNodeRequirement buffer) via CheckRequirements.
  • If valid, deducts the cost from points, creates an Unlock event entity using an EndFrameBarrier command buffer, and sends telemetry (Telemetry.DevNodePurchased) with the node prefab. Note: Unlock events are emitted as entities (Unlock + Event) and will be processed elsewhere.

  • private static bool CheckRequirements(DynamicBuffer<DevTreeNodeRequirement> requirements, ComponentLookup<Locked> locked) : System.Boolean
    Evaluates a node's requirements buffer: for each requirement that references a node, returns true (indicating a failed check / cannot purchase) immediately if that required node is not enabled (i.e., locked.HasEnabledComponent(requirement.m_Node) is false). If there are no referenced requirements, returns true/false according to logic in source: the method returns !flag where flag tracks if any requirement references were present — effectively treating nodes with no requirements as satisfied. (Careful: method returns true to indicate a failed check early; see usage in Purchase where it negates or uses appropriately.)

  • private static bool CheckService(Entity service, ComponentLookup<Locked> locked) : System.Boolean
    Returns true if the service is either Entity.Null or the service entity is not enabled (i.e., purchasable/unlocked). This validates that the service prereq for a node is available.

  • [MethodImpl(MethodImplOptions.AggressiveInlining)] private void __AssignQueries(ref SystemState state) : System.Void
    Compiler-generated helper for query assignment. In the provided code it constructs and disposes an EntityQueryBuilder — part of ECS/IL generation and not typically used directly.

  • protected override void OnCreateForCompiler() : System.Void
    Compiler-time setup invoked to assign queries/handles. Calls __AssignQueries and __TypeHandle.__AssignHandles to initialize ComponentLookup and ComponentTypeHandle instances used later.

Inner types

  • private struct AppendPointsJob : IJob
    Burst-compiled job that applies milestone-reached events to the DevTreePoints singleton chunk. Fields:
  • NativeList m_Chunks — the chunk list for the DevTreePoints singleton.
  • [ReadOnly] NativeList m_MilestoneReached — list of reached milestone events.
  • [ReadOnly] ComponentLookup m_Milestones — lookup to retrieve MilestoneData.devTreePoints where available.
  • ComponentTypeHandle m_PointsType — component type handle to access DevTreePoints in the chunk. Methods:
  • Execute() — reads current points, iterates m_MilestoneReached and adds points from MilestoneData (or default via GetDefaultPoints) and writes back the new DevTreePoints.
  • private int GetDefaultPoints(int level) — returns a default point value based on milestone level: 0 for <=0, 10 for >=19, otherwise (level+1)/2 + 1.

  • private struct TypeHandle
    Holds cached handles/lookups for components used by the system:

  • ComponentLookup (read-only)
  • ComponentTypeHandle (read/write)
  • ComponentLookup (read-only)
  • ComponentLookup (read-only) Method:
  • __AssignHandles(ref SystemState state) — initializes the above lookups/handles with state.GetComponentLookup / GetComponentTypeHandle calls.

Usage Example

[Preserve]
protected override void OnCreate()
{
    base.OnCreate();
    // This system requires a DevTreePoints singleton to exist.
    // You can initialize the starting points elsewhere by setting:
    var devTreeSystem = World.GetOrCreateSystemManaged<Game.City.DevTreeSystem>();
    devTreeSystem.points = 5; // set starting points (if DevTreePoints exists)
}

// Purchasing by prefab:
var devTreeSystem = World.GetOrCreateSystemManaged<Game.City.DevTreeSystem>();
DevTreeNodePrefab nodePrefab = /* obtain prefab */;
devTreeSystem.Purchase(nodePrefab);

// Purchasing by entity:
Entity nodeEntity = /* prefab system or other source */;
devTreeSystem.Purchase(nodeEntity);

// The system will automatically add points when milestones are reached (via MilestoneReachedEvent).

Notes and recommendations: - The system relies on the presence of a DevTreePoints singleton entity to operate; use RequireForUpdate ensures the system only runs when that singleton exists. - Purchases enqueue Unlock events through EndFrameBarrier's command buffer so unlocking is deferred and safe with respect to current frame entity modifications. - The AppendPointsJob uses asynchronous ToArchetypeChunkListAsync / ToComponentDataListAsync patterns; be careful to handle Dependencies correctly if you schedule additional jobs that read/write the same components. - Telemetry.DevNodePurchased is called when a purchase succeeds; ensure telemetry systems are available or mocked during testing.