INI Data, Object Templates, and Modules
Generals does not encode the behavior of every unit as a unique C++ class. Much of the game is authored declaratively, then assembled at runtime through templates and modules. An INI object definition is therefore not merely a static data record. It is an instruction for constructing an object whose runtime behavior emerges from a collection of specialized components.
Authored records and runtime instances
The common thing layer contains the key construction files: Common/Thing/ThingTemplate.cpp, ThingFactory.cpp, and ModuleFactory.cpp. A template represents a reusable authored description. The factory creates an instance with appropriate type information and module composition. Base classes such as Module.cpp and DrawModule.cpp establish the common shape through which drawing, behavior, updates, and other concerns attach to an object.
It is useful to distinguish the lifetime of the definition from the lifetime of its instances. Templates can be parsed and retained as shared content descriptions. Instances carry mutable state: current health, position, target, command state, weapon cooldowns, and the state internal to their active modules. If T is a template and I_i is an instance, the construction relation can be expressed as:
$$ I_i = \operatorname{Instantiate}(T, q_i), $$
where q_i is the spawn-specific state. Two tanks may share the same template and module types while differing completely in q_i.
template record
name, geometry, weapon definitions, behavior declarations
│
▼
factory creates object instance
│
├── body and health state
├── locomotion state
├── weapon state
├── AI/update behavior
└── draw/client representation
This is a simplified construction diagram, not a literal method signature. It captures the important architectural fact: authored declarations select implementation, while runtime instances own changing state.
Modules avoid a combinatorial inheritance tree
A unit can combine a draw module, body, locomotion, weapons, AI updates, production behavior, or custom death behavior without requiring a separate inheritance tree for every combination. In abstract form, an object is assembled from a set of modules:
$$ O = (B, D, L, W, A, \ldots), $$
where B is body state, D is drawing-related behavior, L is locomotion, W is weapons, and A is an update or AI behavior. The ellipsis matters. The system can admit purpose-built modules without forcing unrelated objects to inherit their data and code.
The object is a host for cooperating components, not a giant subclass whose source file contains every feature. The modular boundary also makes source navigation more precise. A visual anomaly begins at the draw module or client representation. A special death event begins at a behavior module. A firing problem begins at weapon and targeting state. The template identifies the selected components, but it does not replace their implementation.
From declaration to behavior
When a unit behaves unexpectedly, the first question is not “which unit class implements this?” The more productive question is “which template entry selected which module, and what object state does that module inspect?” A weapon effect may be found under GameLogic/Object/Weapon, a tactical update under GameLogic/AI, and a special unit action under GameLogic/Object/Behavior. Purpose-built behavior implementations, including SpawnBehavior.cpp, FlightDeckBehavior.cpp, and PropagandaCenterBehavior.cpp, exist because those features can be selected by data.
An investigation can be conducted as a fixed sequence:
1. Identify the object template named by the map, script, or production action.
2. Record the behavior or module declarations relevant to the observed effect.
3. Locate the factory and module type that realizes that declaration.
4. Inspect the module's state predicates and update path.
5. Follow any command, weapon, or client handoff out of that module.
The order matters. Starting in a random behavior implementation can establish what a module is capable of, but it does not establish that the particular object selected it.
What INI can and cannot change
The modular design clarifies the boundary of modding. Altering a template can select and tune behavior exposed by the compiled engine. It can adjust the composition of existing implementation. It cannot define a new native behavior type merely by inventing an INI label, because the factory still needs a compiled type that recognizes and constructs that label.
INI is powerful because it composes existing implementation, not because it replaces implementation. That boundary is especially useful when evaluating a proposed modification: configuration can redirect existing data flow, but a new state machine, targeting algorithm, or renderer feature requires code at the corresponding layer.