Architectural Prototype: A modular, data-driven simulation designed to model resource allocation (Health/Mana), priority dispatching, and event handling in a real-time environment.
Note: Currently implemented in GDScript. The architecture is intentionally designed for a planned migration to C# / .NET to leverage strict typing, Interfaces, and LINQ.
This project simulates a "Central Dispatcher" (The Player) tasked with managing the integrity of multiple independent Nodes (Agents) under varying stress loads.
The primary engineering goal was to decouple Logic from Data. Abilities, Stress Events, and Unit Configurations are defined as abstract resources. This allows the simulation engine to process arbitrary effects without requiring changes to the source code—a pattern essential for scalable software.
To manage the complex behavior of autonomous units (e.g., Idle
Code Highlight: Dynamic State Injection
The State Machine uses a reflection-like approach to automatically discover and initialize its state nodes at runtime. This allows for loose coupling between the controller and its states.
class_name StateMachine
extends Node
# Dependency Injection via 'init'
func init(entity: Node):
owner_entity = entity
# Automatically find State nodes attached to this object
for child in get_children():
if child is State:
# Register states in a Dictionary for O(1) access
states[child.name.to_lower()] = child
child.Transitioned.connect(on_child_transition)
if child.has_method("init"):
child.init(entity)
Instead of hard-coding logic (e.g., func cast_fireball()), the system uses a generic EffectData class. This class acts as a configuration container. The engine reads this data to execute the logic, effectively implementing the Strategy Pattern via data.
class_name EffectData
extends Resource
# Enums define strict types for the logic engine
@export var effect_type: EffectEnums.EffectType = EffectEnums.EffectType.HEAL
@export var trigger: EffectEnums.TriggerType = EffectEnums.TriggerType.ON_CAST
@export var target: EffectEnums.TargetType = EffectEnums.TargetType.TARGET
# Configurable logic parameters
@export var is_aoe: bool = false
@export var value: int = 0
@export var interval: float = 0.0
@export var proc_chance: float = 0.0
To visualize the status of monitored nodes, the system must dynamically map data entities to specific UI slots based on their role configuration.
Code Highlight: Dictionary-based Slot Assignment
The system uses a mapping algorithm to assign entities to grid slots. This separates the logical role of a unit (e.g., "Tank" priority) from its visual representation.
# Inside LayoutManager.gd
func setup_layout():
# Define mapping rules: Which Role goes to which Grid Index?
var role_map := {
MemberClassData.RoleType.TANK: [1], # Priority Slot
MemberClassData.RoleType.OFFTANK: [3],
MemberClassData.RoleType.MELEE: [5, 7, 9],
MemberClassData.RoleType.RANGED: [0, 2, 4, 6, 8]
}
# Algorithm: Assign members to slots based on available indices
var frames_by_index = []
frames_by_index.resize(10)
for member in active_roster:
# Fetch allowed slots for this member's role
var indices = role_map.get(member.current_role, [])
for idx in indices:
if frames_by_index[idx] == null:
frames_by_index[idx] = create_container(member)
break
The system constantly evaluates the state of the group to make automated decisions. Efficient filtering methods are used to query the dataset without unnecessary iterations.
func get_critical_node() -> MemberClassData:
var critical_nodes = []
for member in active_roster:
# Check integrity threshold (Health < Max)
if member.current_health < member.max_health:
critical_nodes.append(member)
if critical_nodes.size() == 0:
return null
# Return a random node from the critical subset
return critical_nodes[randi() % critical_nodes.size()]
The project serves as a prototype for a high-performance C# implementation. Key refactoring goals include:
- Interfaces (
IState,IEffect): To enforce stricter contracts than GDScript's duck-typing. - LINQ: To replace manual loops (like in
get_critical_node) with expressive queries:
- Example:
roster.Where(m => m.Health < m.MaxHealth).OrderBy(m => m.Health).First();
- Events: Replacing Godot Signals with standard C# Events / Delegates.
- System Architecture: Designing decoupled systems using FSM and Signals.
- Algorithm Design: Implementing mapping and filtering logic for dynamic data sets.
- Data Modeling: Structuring complex simulation data into reusable Resources.
- Tooling: Using the Editor as a CMS for defining simulation logic.