<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eric</title>
    <description>The latest articles on DEV Community by Eric (@halfcomplete).</description>
    <link>https://dev.to/halfcomplete</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3551349%2F43034f00-a678-43f3-9851-a64c0d9cb52d.png</url>
      <title>DEV Community: Eric</title>
      <link>https://dev.to/halfcomplete</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/halfcomplete"/>
    <language>en</language>
    <item>
      <title>Behaviour-based Object Composition in Simulations</title>
      <dc:creator>Eric</dc:creator>
      <pubDate>Sun, 02 Nov 2025 10:31:49 +0000</pubDate>
      <link>https://dev.to/halfcomplete/behaviour-based-object-composition-in-simulations-hhi</link>
      <guid>https://dev.to/halfcomplete/behaviour-based-object-composition-in-simulations-hhi</guid>
      <description>&lt;p&gt;Since my first post on dev.to last week, I've had time to take a deeper look at how BOCS could work for &lt;strong&gt;reactive and dynamic simulations&lt;/strong&gt;. This goes past its previous applications in narrative games, applying the same principles and code, just in a different environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So how does the Behaviour and Module pattern work in simulations?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  General Idea Recap
&lt;/h2&gt;

&lt;p&gt;Like in narrative games, BOCS for simulations focuses around Behaviours. Each entity or object in the simulation contains a number of Behaviours that define how it, well, behaves. These Behaviours are then acted upon by external systems through an interface (the Modules that each Behaviour implements). &lt;strong&gt;For a more detailed breakdown of how BOCS works in general, check out &lt;a href="https://dev.to/halfcomplete/an-object-composition-system-for-reactive-worlds-bocs-449h"&gt;my first article introducing BOCS&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Some examples
&lt;/h2&gt;

&lt;p&gt;Let's take a look at some use cases / example objects where BOCS can be used to compose them efficiently. These are mostly taken from my WIP Mars colony simulation (fittingly named &lt;em&gt;Incident Report&lt;/em&gt;). First, some quick example Behaviours (each one represents a distinct capability):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PowerConsumerBehaviour&lt;/code&gt; - can use electrical energy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HeatSourceBehaviour&lt;/code&gt; - can emit thermal energy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NetworkNodeBehaviour&amp;lt;T&amp;gt;&lt;/code&gt; - can connect to a resource network / graph as a node.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PowerStorerBehaviour&lt;/code&gt; - can store power.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OxygenConsumerBehaviour&lt;/code&gt; - can use oxygen.&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These, and more, let us create &lt;strong&gt;highly specific entities without hardcoding dozens of special cases.&lt;/strong&gt; Now let's dive into some deeper examples.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Example: A Simple Power Generator
&lt;/h3&gt;

&lt;p&gt;Say we want a small power generator that converts stored fuel into electricity.&lt;/p&gt;

&lt;p&gt;Behaviours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FuelConsumerBehaviour&lt;/code&gt; (implements &lt;code&gt;IConsumer&amp;lt;Fuel&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PowerSourceBehaviour&lt;/code&gt; (implements &lt;code&gt;ISource&amp;lt;Power&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PowerNetworkNodeBehaviour&lt;/code&gt; (implements &lt;code&gt;INetworkNode&amp;lt;Power&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FuelNetworkNodeBehaviour&lt;/code&gt; (implements &lt;code&gt;INetworkNode&amp;lt;Fuel&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example &lt;em&gt;FuelConsumerBehaviour&lt;/em&gt; implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FuelConsumerBehaviour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConsumer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fuel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Fuel&lt;/span&gt; &lt;span class="n"&gt;_storedFuel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Fuel&lt;/span&gt; &lt;span class="n"&gt;_capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FuelConsumerBehaviour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_storedFuel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Attempt to withdraw up to `amount`. Returns actual withdrawn amount.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Fuel&lt;/span&gt; &lt;span class="nf"&gt;TryConsume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Fuel&lt;/span&gt; &lt;span class="n"&gt;fuel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fuel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Fuel&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fuel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_storedFuel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_storedFuel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// notify fuel change listeners&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;taken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is powerful because the Behaviours &lt;strong&gt;don't know anything about systems or simulation loops&lt;/strong&gt; - they only coordinate the Modules it composes, making testing a lot easier.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Example: Heat Generator (Power turns into Heat)
&lt;/h3&gt;

&lt;p&gt;A heat generator that converts electrical power into thermal energy.&lt;br&gt;
It could be a reactor coolant pump, a habitat heater, or even a solar array radiating heat.&lt;/p&gt;

&lt;p&gt;Behaviours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PowerConsumerBehaviour&lt;/code&gt; (implements &lt;code&gt;IConsumer&amp;lt;Power&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HeatSourceBehaviour&lt;/code&gt; (implements ISource`)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PowerNetworkNodeBehaviour&lt;/code&gt; (implements &lt;code&gt;INetworkNode&amp;lt;Power&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ThermalNetworkNodeBehaviour&lt;/code&gt; (implements &lt;code&gt;INetworkNode&amp;lt;Thermal&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any system that ticks all &lt;code&gt;IConsumer&amp;lt;Power&amp;gt;&lt;/code&gt; and &lt;code&gt;ISource&amp;lt;Heat&amp;gt;&lt;/code&gt; interfaces will automatically handle this generator correctly, no special cases required.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Example: A Power Storage Unit (Battery)
&lt;/h3&gt;

&lt;p&gt;A battery just contains a &lt;code&gt;PowerStorerBehaviour&lt;/code&gt;, and (if you think about it) a &lt;code&gt;PowerStorerBehaviour&lt;/code&gt; is just consuming given power and providing requested power. In other words, it's simply an &lt;code&gt;IConsumer&lt;/code&gt; and an &lt;code&gt;ISource&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Behaviours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PowerStorerBehaviour&lt;/code&gt; (implements &lt;code&gt;IConsumer&amp;lt;Power&amp;gt;&lt;/code&gt; and &lt;code&gt;ISource&amp;lt;Power&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PowerNetworkNodeBehaviour&lt;/code&gt; (implements &lt;code&gt;INetworkNode&amp;lt;Power&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example &lt;em&gt;PowerStorerBehaviour&lt;/em&gt; Implementation:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PowerStorerBehaviour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConsumer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Power&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;ISource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Power&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;_capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;_maxChargeRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;_maxDischargeRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;_charge&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PowerStorerBehaviour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;maxChargeRate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;maxDischargeRate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_maxChargeRate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maxChargeRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_maxDischargeRate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maxDischargeRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_charge&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Stored&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_charge&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Capacity&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// "Consume" some amount of energy&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;Consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dtSecs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// consumption logic...&lt;/span&gt;
        &lt;span class="c1"&gt;// notify listeners...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// "Extract" some amount of energy&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Energy&lt;/span&gt; &lt;span class="nf"&gt;Extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dtSecs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// extraction logic...&lt;/span&gt;
        &lt;span class="c1"&gt;// notify listeners...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// support for efficiency losses and degradation...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;All we need to do to modify the battery's capacity, charge/discharge rate, or anything else, is &lt;strong&gt;change the parameters&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Example: An Air Processor
&lt;/h3&gt;

&lt;p&gt;This takes power and produces oxygen. We can also make it consume carbon dioxide - simply add a &lt;code&gt;CarbonDioxideConsumerBehaviour&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Behaviours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PowerConsumerBehaviour&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OxygenSourceBehaviour&lt;/code&gt; (implements &lt;code&gt;ISource&amp;lt;Oxygen&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PowerNetworkNodeBehaviour&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AtmosphereNetworkNodeBehaviour&lt;/code&gt; (implements &lt;code&gt;INetworkNode&amp;lt;Atmosphere&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BOCS makes this design emergent quickly and easily, and what's amazing is that the simulation just sees a network of &lt;code&gt;IConsumer&lt;/code&gt; and &lt;code&gt;ISource&lt;/code&gt; behaviours doing their job.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Example: A Room
&lt;/h3&gt;

&lt;p&gt;Even rooms in &lt;em&gt;Incident Report&lt;/em&gt; can themselves be treated as BOCS objects with behaviours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AtmosphereContainerBehaviour&lt;/code&gt; - stores and tracks gas composition, temperature, and pressure.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AtmosphereNetworkNodeBehaviour&lt;/code&gt; - connects to the base’s air ducts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ThermalNetworkNodeBehaviour&lt;/code&gt; - allows conduction and radiation exchange.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PhysicalNetworkNodeBehaviour&lt;/code&gt; - simulates real, physical connections, allowing for moving entities to traverse in and out of this object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example &lt;em&gt;AtmosphereContainerBehaviour&lt;/em&gt; Implementation (taken straight from &lt;em&gt;Incident Report&lt;/em&gt; with some removed functionality, including some data types and documentation):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Uses ideal-gas approximations and per-gas mole bookkeeping.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AtmosphereContainerBehaviour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BehaviourBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Physical constants / defaults&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8.314462618&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// J / (mol K) - universal gas constant&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;DefaultMolarHeatCapacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;29.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// J / (mol K) approximate Cp for air&lt;/span&gt;

    &lt;span class="c1"&gt;// State&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;_volumeM3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// fixed container volume (m^3)&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_moles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// gas name to moles&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;_temperatureK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// absolute temperature (K)&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AtmosphereContainerBehaviour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;volumeM3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;initialTempK&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;293.15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;volumeM3&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentOutOfRangeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;volumeM3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;_volumeM3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;volumeM3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_temperatureK&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initialTempK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Read-only properties useful for networks and telemetry:&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Volume&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_volumeM3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;TemperatureK&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_temperatureK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;TotalMoles&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Ideal gas law: P = n R T / V&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;PressurePa&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TotalMoles&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TotalMoles&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;_temperatureK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;_volumeM3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Per-gas helpers&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;GetMoles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;MoleFraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TotalMoles&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;GetMoles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;TotalMoles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;PartialPressure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;MoleFraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PressurePa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Atmosphere API used by atmosphere network adapters&lt;/span&gt;

    &lt;span class="c1"&gt;// Add gas (moles). Returns moles actually added (accepts all in this simple model).&lt;/span&gt;
    &lt;span class="c1"&gt;// Network can call this to push gas into the room.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;AddGas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;moles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;moles&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;moles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;moles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove up to `moles` of a named gas. Returns how many moles were removed.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;RemoveGas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;moles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;moles&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;moles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Transfer a portion (by moles) of the container's entire mixture out to another container.&lt;/span&gt;
    &lt;span class="c1"&gt;// Network adapters (or Physical node on opening) can call this to equalize or move gas.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;TransferMixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;molesToTransfer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;StringComparer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;molesToTransfer&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;TotalMoles&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// scale each gas according to current mole fraction&lt;/span&gt;
        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;fraction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;molesToTransfer&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;TotalMoles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gas&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fraction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_moles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Thermal API used by thermal network adapters&lt;/span&gt;

    &lt;span class="c1"&gt;// Apply energy (J). Positive adds heat, negative cools. Returns energy actually applied (here we accept all).&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;ApplyHeatEnergy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;energyJ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;energyJ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1e-12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;cpTotal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TotalMoles&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;DefaultMolarHeatCapacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// J / K&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cpTotal&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// no gas -&amp;gt; energy raises empty volume (ignored in simple model)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;deltaT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;energyJ&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;cpTotal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_temperatureK&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;deltaT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// clamp temperature to physical bounds and record energy balance...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;energyJ&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Exchange energy with another container (energy flows from this -&amp;gt; target).&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;ExchangeEnergyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AtmosphereContainerBehaviour&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;energyJ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Simple direct transfer (no losses)&lt;/span&gt;
        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;applied&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;energyJ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;energyJ&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// placeholder&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ApplyHeatEnergy&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="n"&gt;applied&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ApplyHeatEnergy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;applied&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;applied&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Utility / Debugging&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"Atmosphere(V=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_volumeM3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;m^3, T=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_temperatureK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;F1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;K, P=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PressurePa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;F1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;Pa, n=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TotalMoles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;F3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;mol)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Omitted complexities:&lt;/span&gt;
    &lt;span class="c1"&gt;// - Use species-specific molar heat capacities and non-ideal-gas corrections (esp. under high pressure).&lt;/span&gt;
    &lt;span class="c1"&gt;// - Track mass vs moles if condensables (water) are present, handle phase changes.&lt;/span&gt;
    &lt;span class="c1"&gt;// - Support diffusive exchange, leak rates through small openings, and door/windows transient flows.&lt;/span&gt;
    &lt;span class="c1"&gt;// - Proper unit conversion helpers and notify listeners to pressure/temperature change&lt;/span&gt;
    &lt;span class="c1"&gt;// - Provide per-gas partial heat capacities for accurate thermal coupling.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This makes rooms &lt;strong&gt;active participants in the simulation&lt;/strong&gt; rather than passive data blobs like they would be in an ECS.&lt;/p&gt;




&lt;h3&gt;
  
  
  Advanced Example: A Reactor Core System
&lt;/h3&gt;

&lt;p&gt;Let’s simulate a fusion reactor that &lt;strong&gt;generates energy and heat using fuel and power, with automatic safety shutdown when things go wrong&lt;/strong&gt;. NOTE: the code snippets are simplified to get the general idea across easier.&lt;br&gt;
Each behaviour is isolated but composed to create the overall system.&lt;/p&gt;

&lt;p&gt;Behaviours:&lt;br&gt;
&lt;em&gt;PowerSourceBehaviour&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PowerSourceBehaviour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ISource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Power&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Power&lt;/span&gt; &lt;span class="n"&gt;_maxPowerExtractionKW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Power&lt;/span&gt; &lt;span class="n"&gt;_currentPowerExtraction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Power&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromKW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Power&lt;/span&gt; &lt;span class="n"&gt;MaxPowerExtraction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_maxPowerExtractionKW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Percentage&lt;/span&gt; &lt;span class="n"&gt;_extractionEfficiency&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Percentage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Percentage&lt;/span&gt; &lt;span class="n"&gt;ExtractionEfficiency&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_extractionEfficiency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PowerSourceBehaviour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Power&lt;/span&gt; &lt;span class="n"&gt;maxPowerUsageKW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_maxPowerExtractionKW&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maxPowerUsageKW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Energy&lt;/span&gt; &lt;span class="nf"&gt;Extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dtSecs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// _currentPowerExtraction is in kW, so convert to W, then multiply by time (s) for Joules&lt;/span&gt;
            &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;joules&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;_currentPowerExtraction&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;_extractionEfficiency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1000.0&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dtSecs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// notify listeners...&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Energy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromJoules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joules&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;HeatSourceBehaviour&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HeatSourceBehaviour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ISource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Heat&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// various fields and properties&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Temperature&lt;/span&gt; &lt;span class="nf"&gt;Extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dtSecs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Power&lt;/span&gt; &lt;span class="n"&gt;power&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Temperature&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Temperature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// logic to calculate how much heat is emitted&lt;/span&gt;
        &lt;span class="c1"&gt;// logic to notify any heat stat listener behaviours&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;FuelConsumerBehaviour&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FuelConsumerBehaviour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConsumer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fuel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// various fields and properties&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Fuel&lt;/span&gt; &lt;span class="nf"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dtSecs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Fuel&lt;/span&gt; &lt;span class="n"&gt;fuel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Fuel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// logic to calculate how much fuel is consumed after using this object for dtSecs seconds&lt;/span&gt;
        &lt;span class="c1"&gt;// notify listeners...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fuel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;PowerConsumerBehaviour&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PowerConsumerBehaviour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IConsumer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Power&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// various fields and properties&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Power&lt;/span&gt; &lt;span class="nf"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;dtSecs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Power&lt;/span&gt; &lt;span class="n"&gt;power&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Fuel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// logic to calculate how much power is consumed after using this object for dtSecs seconds&lt;/span&gt;
        &lt;span class="c1"&gt;// notify listeners...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;power&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;HeatStatListenerBehaviour&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HeatStatListenerBehaviour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IActOnParentStatUpdated&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Heat&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;BOCSObject&lt;/span&gt; &lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Temperature&lt;/span&gt; &lt;span class="n"&gt;_heatThreshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Temperature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;_emergencyTriggered&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// instantiate logic&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnParentTargetStatUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentHeat&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_heatThreshold&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;_emergencyTriggered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_emergencyTriggered&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisableBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PowerSourceBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisableBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HeatSourceBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// notify listeners...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Composing the Object&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BOCSObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FusionReactor"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PowerSourceBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HeatSourceBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FuelConsumerBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PowerConsumerBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HeatStatListenerBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;power&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PowerSourceBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;heat&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HeatSourceBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h3&gt;
  
  
  What’s Happening
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PowerSourceBehaviour&lt;/code&gt; produces power every tick.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HeatSourceBehaviour&lt;/code&gt; reacts to power output and calls any stat listeners.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FuelConsumerBehaviour&lt;/code&gt; uses up fuel to generate power.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PowerConsumerBehaviour&lt;/code&gt; uses up some power to use fuel and generate power.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HeatStatListenerBehaviour&lt;/code&gt; listens for updates to its parent Reactor heat stat and can disable all functionality if it gets too hot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system is &lt;strong&gt;modular, extensible, testable&lt;/strong&gt;: however, just by themselves, Behaviours can't do much. It is up to external systems to activate these Behaviours and to coordinate and use them properly. Some extra code could be included to automate this coordination such that only a public method &lt;code&gt;Tick()&lt;/code&gt; could be exposed, which gets the Behaviours to &lt;strong&gt;act with a single call&lt;/strong&gt;. Furthermore, common objects can be created easily (including all their Behaviours added) through a &lt;strong&gt;factory pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the reactor, we could later add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;HeatConsumerBehaviour&lt;/code&gt; (a coolant pump) that reduces heat when active.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;PowerStorerBehaviour&lt;/code&gt; (a battery) that captures unused power.&lt;/li&gt;
&lt;li&gt;Another &lt;code&gt;HeatStatListenerBehaviour&lt;/code&gt; that re-enables systems after cooldown, or just modify the current &lt;code&gt;HeatStatListenerBehaviour&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Now, Why Use BOCS In These Instances?
&lt;/h2&gt;

&lt;p&gt;BOCS gives every object in the simulation a composable identity.&lt;br&gt;
We no longer need to define hundreds of bespoke classes like &lt;code&gt;Heater&lt;/code&gt;, &lt;code&gt;Battery&lt;/code&gt;, or &lt;code&gt;OxygenPump&lt;/code&gt;.&lt;br&gt;
Instead, we just combine Behaviours:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foub7ne9xh3jyy5gzneux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foub7ne9xh3jyy5gzneux.png" alt="Incident Report BOCS Example Summary Table" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  When NOT to Use BOCS
&lt;/h2&gt;

&lt;p&gt;BOCS is brilliant for objects with identity or complex emergent behaviour: machines, rooms, or actors that &lt;strong&gt;do something&lt;/strong&gt;.&lt;br&gt;
But for purely structural or relational elements, such as connections, BOCS &lt;em&gt;can&lt;/em&gt; be overkill.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Network Connections
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Incident Report&lt;/em&gt; has &lt;em&gt;Networks&lt;/em&gt; which handle resource transfer between  &lt;code&gt;NetworkNodeBehaviour&lt;/code&gt;s via &lt;code&gt;NetworkConnections&lt;/code&gt;. However, a pipe between two nodes or a wire between two terminals doesn’t need Behaviours.&lt;br&gt;
It’s just a &lt;strong&gt;structural link&lt;/strong&gt; with defined flow characteristics:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NetworkConnection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;NetworkNodeBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;NetworkNodeBehaviour&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;MaxFlowRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;Resistance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;CurrentFlow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NetworkConnectionSegment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;segments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We don’t need to attach this to a BOCS object, with all its overhead and extra code - there's no need for a wire to have modular behaviour, or a pipe to have emergent characteristics.&lt;br&gt;
Instead, a NetworkSystem can own and tick all &lt;code&gt;NetworkConnection&amp;lt;T&amp;gt;&lt;/code&gt; instances directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BOCS should handle functional entities, NOT structural edges.&lt;/strong&gt; And that's a really important distinction as well - if the line between using BOCS and not using BOCS is not bolded and solid, &lt;strong&gt;the complexity of a project may increase exponentially&lt;/strong&gt;, a problem that BOCS (when used well) wouldn't usually have.&lt;/p&gt;

&lt;p&gt;You might've also noticed I didn't go into detail the specifics of  &lt;code&gt;NetworkNodeBehaviour&lt;/code&gt; and &lt;code&gt;NetworkConnection&lt;/code&gt;. This is because I haven't fully decided on how they should work in relation to everything else in &lt;em&gt;Incident Report&lt;/em&gt;, and thus haven't fleshed out the implementation details. I will, however, make a post entirely on the Network system in &lt;em&gt;Incident Report&lt;/em&gt; later down the line!&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;At its core, BOCS is about letting objects define what they are by what they can do, &lt;strong&gt;not by which inheritance chain they belong to. When applied to simulations&lt;/strong&gt;, that principle becomes even more powerful. Every entity - from a fusion reactor to a simple air processor - becomes a &lt;strong&gt;living, reactive agent&lt;/strong&gt; in a larger network, capable of communicating, changing, and adapting through composition alone.&lt;/p&gt;

&lt;p&gt;This makes simulations written with BOCS &lt;strong&gt;inherently modular, reactive, and testable&lt;/strong&gt;. You can tweak a single behaviour, add or remove capabilities, or swap modules entirely without touching other code. And because everything is defined in terms of capabilities (&lt;code&gt;ISource&lt;/code&gt;, &lt;code&gt;IConsumer&lt;/code&gt;, &lt;code&gt;INetworkNode&lt;/code&gt;...), it encourages systems thinking - building worlds where every piece, &lt;em&gt;big or small&lt;/em&gt;, can participate in the larger emergent story of the simulation.&lt;/p&gt;

&lt;p&gt;But that flexibility comes with discipline. As I mentioned above, BOCS isn’t for everything. The trick is to use it where identity matters, where an &lt;strong&gt;object’s purpose, reaction, and behaviour make it need more than just a line in a data table&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What do you think?
&lt;/h3&gt;

&lt;p&gt;Do you see potential in using BOCS for simulation frameworks or complex game systems?&lt;br&gt;
How would you structure the coordination layer - the part that &lt;em&gt;“ticks”&lt;/em&gt; all Behaviours and manages how they talk to each other?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’d love to hear your perspective, whether you’ve worked with ECS, reactive systems, or your own flavour of modular architecture!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>software</category>
      <category>csharp</category>
    </item>
    <item>
      <title>An Object Composition System for Reactive Worlds: BOCS</title>
      <dc:creator>Eric</dc:creator>
      <pubDate>Mon, 27 Oct 2025 05:18:33 +0000</pubDate>
      <link>https://dev.to/halfcomplete/an-object-composition-system-for-reactive-worlds-bocs-449h</link>
      <guid>https://dev.to/halfcomplete/an-object-composition-system-for-reactive-worlds-bocs-449h</guid>
      <description>&lt;h2&gt;
  
  
  BOCS: A Behaviour-based Game Architecture for Narrative Worlds
&lt;/h2&gt;

&lt;p&gt;I started building my narrative-driven text RPG &lt;em&gt;Ashborne&lt;/em&gt; a few months ago, and I noticed a problem pretty quickly: I needed more than a traditional entity architecture such as the Entity Component System (ECS). I needed a system that would allow characters, objects, and even the world itself, to react and evolve.&lt;/p&gt;

&lt;p&gt;So that’s how I ended up building BOCS, the Behaviour-based Object Composition System, a design philosophy I'd love to introduce you to today. It helps you build reactive, reconfigurable game entities that can change mid-game - perfect for RPGs, simulations, or systemic worlds where things evolve dynamically.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;BOCS rethinks the “entity” model through a different lens, inspired by existing systems and philosophies (like ECS).&lt;/p&gt;

&lt;p&gt;At its heart, a &lt;em&gt;BOCSObject&lt;/em&gt; is composed of &lt;strong&gt;Behaviours&lt;/strong&gt;, and each &lt;strong&gt;Behaviour&lt;/strong&gt; implements one or more &lt;strong&gt;Modules&lt;/strong&gt; (which are interfaces).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsccewn7wohjdtoyyzxar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsccewn7wohjdtoyyzxar.png" alt="4 simple examples for how BOCS is structured" width="800" height="684"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each Module defines what the &lt;em&gt;Behaviour&lt;/em&gt; that implements it can do, like a contract; this is very similar to how interfaces are used in other systems. They are also used by external systems to identify the Behaviour that implements it.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;BOCSObject&lt;/em&gt; is simply an abstract class with a list of Behaviours and methods for managing those Behaviours.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// A base class for all objects in the BOCS system, with support for adding and managing behaviours.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BOCSObjectBase&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... (other config fields + properties)&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// A dictionary mapping Module types to lists of Behaviours that implement those Modules.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Behaviours&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="n"&gt;Behaviours&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AddBehaviour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BehaviourBase&lt;/span&gt; &lt;span class="n"&gt;behaviour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ... (adds the given Behaviour to the Behaviours dictionary)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Remove all Behaviours in this object that implement the Module T.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;The target Module, of which all implementations are to be removed in the Behaviours of this object.&amp;lt;/typeparam&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;RemoveAllBehavioursOfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
        &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BehaviourBase&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;targetBehaviours&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Behaviours&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;

            &lt;span class="c1"&gt;// ... (removes all instances of the targetBehaviours from Behaviours and returns the number removed)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;The Behaviour to check for presence in this object.&amp;lt;/typeparam&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;A bool representing whether this object contains the Behaviour specified.&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;HasBehaviourOfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
        &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="err"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;The Module T to get all implementations of.&amp;lt;/typeparam&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;An IEnumerable that contains all the Behaviours that implement the provided Module T.&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="nc"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetAllBehavioursOfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
        &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="err"&gt;}&lt;/span&gt;

        &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nc"&gt;endregion&lt;/span&gt; &lt;span class="n"&gt;Behaviours&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something super important here is that Behaviours are tracked  using a &lt;strong&gt;mutable list&lt;/strong&gt;. This means that Behaviours of an object can be modified at runtime, something that plain old static inheritance cannot provide. Like I said before, a flaming sword could have its &lt;code&gt;OnUseApplyStatusEffectToTargetBehaviour&lt;/code&gt; (yes, it is a lengthy name!) that applies a flaming status effect to enemies when it is used removed if the sword is dropped into water.&lt;/p&gt;

&lt;p&gt;It’s modular, reactive and dynamic, allowing objects to be reconfigured at runtime - something that many other architectures don't allow for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Interaction with Systems
&lt;/h2&gt;

&lt;p&gt;The big stuff - the "magic" - comes from how systems interact with Behaviours. Because each Behaviour implements one or more Modules (interfaces), a system can simply &lt;strong&gt;grab all Behaviours that implement a certain Module and call the corresponding methods within the Behaviour&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's tackle this with an example. Say your RPG has a helmet item that gives the player some extra defense when equipped. The BOCS structure would look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19ojv7upnmg5iqhn3peq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19ojv7upnmg5iqhn3peq.png" alt="The BOCS structure of a helmet" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how all of the Behaviours are super &lt;em&gt;general and versatile&lt;/em&gt;? Instead of having dozens of &lt;code&gt;OnEquipChangePlayerStatBehaviour&lt;/code&gt; subclasses that each change a different player stat when equipped, we &lt;strong&gt;just have one&lt;/strong&gt;, with an extra field determining the type of stat to change.&lt;/p&gt;

&lt;p&gt;Furthermore, any system or event can call the helmet's &lt;code&gt;EquippableBehaviour&lt;/code&gt;'s &lt;code&gt;Equip()&lt;/code&gt; method, which would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First "equip" the helmet onto the player's body (but no stats or effects occur beyond this, which is handled separately)&lt;/li&gt;
&lt;li&gt;Then search the helmet's other Behaviours for ones that implement &lt;code&gt;IActOnEquip&lt;/code&gt; using its &lt;code&gt;GetAllBehavioursOfType(T)&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;And finally run the interface methods &lt;code&gt;OnEquip()&lt;/code&gt; or &lt;code&gt;OnUnequip&lt;/code&gt; of those found Behaviours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its &lt;em&gt;modular, extensible and flexible&lt;/em&gt;, for any situation. Want the helmet to make the player FASTER instead of slower? Simply replace the status effect type in its &lt;code&gt;OnEquipApplyStatusEffectBehaviour&lt;/code&gt;! Want to do it in the middle of the game? That's ALREADY supported! Want to create a random mix-and-match, absurdly stupid item that has so many functionalities that its completely unique from everything else in the way it can do &lt;em&gt;EVERYTHING&lt;/em&gt;? Just attach &lt;em&gt;every&lt;/em&gt; Behaviour onto it.&lt;/p&gt;

&lt;p&gt;What's more exciting is that this system doesn't just apply to items - in fact, it can be used for &lt;strong&gt;settings, in-game object and NPCs&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;NPCs might have a &lt;strong&gt;DialogueBehaviour&lt;/strong&gt; and an &lt;strong&gt;InventoryBehaviour&lt;/strong&gt; to make it a &lt;em&gt;trader&lt;/em&gt; that the player can't attack (no HealthBehaviour!)&lt;/li&gt;
&lt;li&gt;An object could have an &lt;strong&gt;OpenCloseBehaviour&lt;/strong&gt;, an &lt;strong&gt;InventoryBehaviour&lt;/strong&gt; and a &lt;strong&gt;HealthBehaviour&lt;/strong&gt; to make it a &lt;em&gt;destructible chest&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Or replace the &lt;strong&gt;InventoryBehaviour&lt;/strong&gt; with an &lt;strong&gt;OnOpenTriggerExplosionBehaviour&lt;/strong&gt; (that could be applied to anything that can be opened/closed) to make it a booby-trapped chest&lt;/li&gt;
&lt;li&gt;A throne room could have a &lt;strong&gt;EnterBehaviour&lt;/strong&gt;, an &lt;strong&gt;OnEnterTriggerBattleBehaviour&lt;/strong&gt; and an &lt;strong&gt;OnEnterApplyStatusEffectBehaviour&lt;/strong&gt; to make it a boss battle throne room that gives the player Weakness upon entering&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The possibilities are (almost) endless&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why not use traditional architectures?
&lt;/h2&gt;

&lt;p&gt;Traditional entity architectures, such as the Entity-component-system are great for performance and flexibility, especially in large-scale simulations of thousands of entities.&lt;br&gt;
But when you’re designing a narrative-driven RPG, especially one that reacts to player emotion and choice, &lt;strong&gt;traditional ECS can feel too mechanical and restricting&lt;/strong&gt; in how it separates data and behaviour.&lt;/p&gt;

&lt;p&gt;Let’s break down what some of those limitations look like in practice.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Rigid Inheritance Hierarchies in OOP
&lt;/h3&gt;

&lt;p&gt;In traditional OOP (e.g. Enemy : Character : GameObject), behaviours are &lt;strong&gt;inherited, not composed&lt;/strong&gt;.&lt;br&gt;
That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding new behaviour (say, giving an enemy the ability to fly) forces you to extend or rewrite existing classes.&lt;/li&gt;
&lt;li&gt;The codebase becomes &lt;em&gt;deep, brittle, and hard to reason about&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;You can’t easily “swap” functionality at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In large-scale or systemic games, you need objects that can &lt;strong&gt;change dynamically&lt;/strong&gt; e.g., an NPC turning into an ally, or a torch that becomes a weapon, all without breaking the codebase.&lt;/p&gt;

&lt;p&gt;BOCS replaces inheritance with behaviour composition — objects are built from &lt;strong&gt;interchangeable Behaviours, which themselves implement interfaces for systems to call externally&lt;/strong&gt;. This is very similar to other design philosophies such as the decorator pattern, or composition over inheritance.&lt;/p&gt;

&lt;p&gt;A flaming arrow could lose its fire after being drenched simply by removing its &lt;code&gt;OnUseApplyStatusEffectToTargetBehaviour&lt;/code&gt; at runtime.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Overly Granular or Opaque Systems
&lt;/h3&gt;

&lt;p&gt;ECS is powerful, but also abstract to the point of inaccessibility.&lt;br&gt;
&lt;strong&gt;It’s optimised for performance, not clarity&lt;/strong&gt;. That makes it great for Unity DOTS or simulation-heavy systems... but poor for &lt;em&gt;expressive design&lt;/em&gt; or small team development.&lt;/p&gt;

&lt;p&gt;You end up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Systems” that are hard to follow without a debugger.&lt;/li&gt;
&lt;li&gt;Components that can’t easily express intent (“this NPC is cautious”).&lt;/li&gt;
&lt;li&gt;Designers or writers locked out of the technical loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ECS tends to fragment logic so much that emergent storytelling and contextual systems become &lt;strong&gt;hard to express and debug&lt;/strong&gt;.&lt;br&gt;
BOCS borrows ECS’s composition and data separation, but instead wraps it in a behavioural context, so logic lives where it makes sense.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Tight Coupling Between Systems
&lt;/h3&gt;

&lt;p&gt;In traditional OOP and even many ECS implementations, systems are highly dependent on one another.&lt;br&gt;
For instance, a combat system might directly reference the player’s inventory or stats. And when one system changes, others break. It’s hard to &lt;em&gt;isolate, test, or expand features&lt;/em&gt; independently.&lt;/p&gt;

&lt;p&gt;To solve this, BOCS introduces a &lt;strong&gt;Behaviour-based Event Bus&lt;/strong&gt;, &lt;strong&gt;a dependency injection system&lt;/strong&gt;, and uses interfaces &lt;em&gt;heavily&lt;/em&gt; so that  Behaviours communicate through messages and indirect calls.&lt;/p&gt;

&lt;p&gt;This dramatically improves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modularity&lt;/li&gt;
&lt;li&gt;Testability&lt;/li&gt;
&lt;li&gt;Team parallelisation (multiple people can work on different behaviours safely)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Poor Support for Systemic Design and Emergence
&lt;/h3&gt;

&lt;p&gt;In modern games and simulations, we want emergence, when simple systems interact in complex ways.&lt;br&gt;
But ECS and OOP tend to discourage that naturally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ECS can isolate logic into distant systems.&lt;/li&gt;
&lt;li&gt;OOP hides state behind class hierarchies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interactivity and reactivity (e.g., an NPC responding to weather, or their mood affecting dialogue) is awkward and requires hardcoded exceptions.&lt;/p&gt;

&lt;p&gt;As a solution, BOCS defines a &lt;strong&gt;shared world state with conditional evaluators and behaviour subscriptions&lt;/strong&gt; - Behaviours can listen to any world or local event and react accordingly, without needing bespoke code. So, when it starts raining, a WeatherBehaviour in the current setting would broadcast a OnWeatherChanged event that contains additional info about the new type of weather (rain). All Behaviours implementing the IReactToWeather can then choose to modify themselves; maybe the villager walks home, or the merchant closes shop.&lt;/p&gt;

&lt;p&gt;BOCS makes emergence easy and simple.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Slow Prototyping and Designer Bottleneck
&lt;/h3&gt;

&lt;p&gt;Even in indie teams or educational contexts, the slowest part of development is iteration, especially for non-programmers.&lt;br&gt;
Writers, designers, and testers often need code-level access to tweak dialogue triggers or object logic.&lt;/p&gt;

&lt;p&gt;The key limitation here is that &lt;strong&gt;designers can’t iterate fast&lt;/strong&gt; because game logic is trapped in compiled code.&lt;/p&gt;

&lt;p&gt;Because BOCS separates logic into modular behaviours, it’s easy to expose them to external configuration: JSON, Ink, YAML, etc.&lt;br&gt;
This means designers can define &lt;em&gt;how an object behaves without touching the engine core at all&lt;/em&gt; - not a single drop of programming needed. But of course, unless a proper interface between a user-friendly layer and the data is created, designers will have to directly add behaviour through the chosen data structure, such as JSON or YAML.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR Design Benefits
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Flexibility&lt;/strong&gt;: Behaviours can be added, removed, or swapped mid-game.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modularity&lt;/strong&gt;: Almost any object can be composed with the right pool of Behaviours through mix-and-match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: Each Behaviour has its own lifecycle, reducing coupling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emergent Systems&lt;/strong&gt;: Interactions arise naturally from independent Behaviours listening to shared events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Perfect for games that evolve with player choice or modular content.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Why It Matters
&lt;/h2&gt;

&lt;p&gt;Games like &lt;em&gt;Ashborne&lt;/em&gt; thrive on reactivity, the feeling that the world remembers and responds.&lt;/p&gt;

&lt;p&gt;With BOCS, I’ve found a way to make that technically feasible &lt;strong&gt;without sacrificing modularity or elegance&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I believe systems like this can redefine how narrative-driven games are built, especially those that need to intertwine world logic with player psychology.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;I’m currently using BOCS to power &lt;em&gt;Ashborne’s&lt;/em&gt; NPCs, objects and reactive dialogue systems.&lt;br&gt;
In future versions, I plan to open-source a &lt;strong&gt;full version of BOCS&lt;/strong&gt; as an example for devs who want to build reactive narrative systems too. However, because this is more of a design philosophy than a strict architecture, you and any others can &lt;strong&gt;adapt it to your specific needs and modify it in any way you want&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Also, if anything in this post sounds interesting, feel free to ask me any questions about it. Would you use this system in your own future projects? Why? And is there any other way I could improve it?&lt;br&gt;
I’ll also be sharing progress as I refine the model and test it across multiple genres (one of which will be simulations, something very interesting as ECS already dominates here).&lt;/p&gt;




&lt;h3&gt;
  
  
  Closing Thoughts
&lt;/h3&gt;

&lt;p&gt;Building BOCS taught me that architecture is storytelling, and that if you’re building a game that needs to feel &lt;em&gt;alive&lt;/em&gt;, maybe it’s time to look beyond data and think in terms of &lt;strong&gt;behaviour&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'd also like to emphasise this: BOCS isn’t a replacement for ECS. It’s a reinterpretation. Where ECS optimises for performance, BOCS optimises for expression and reactivity. There are situations in which BOCS is not the way to go!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;EDIT: Take a look at &lt;a href="https://dev.to/halfcomplete/behaviour-based-object-composition-in-simulations-hhi"&gt;my article discussing BOCS in simulations, where ECS usually dominates.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>coding</category>
      <category>architecture</category>
      <category>software</category>
    </item>
  </channel>
</rss>
