Units And Structures
How a tank, an infantry squad, and a power plant share the same Object framework but behave differently via KindOf flags and module loadouts.
A Crusader tank, a Ranger, and a power plant are the same kind of thing to the engine. Each is an Object stamped from a ThingTemplate, carrying a BodyModule, a KindOfMask, and a list of behavior modules. Nothing in the Object class says "I am a building" or "I am a vehicle." The differences live entirely in which kindof bits are set and which modules got attached at construction. Change the bits and the modules and a tank becomes a turret, or a barracks becomes a walking robot.
KindOf as type discriminator
The kindof mask is a bitset, not an int. A template routinely has ten or twelve bits set. ThingTemplate::isKindOf is a simple bit test:
// GeneralsMD/Code/GameEngine/Include/Common/ThingTemplate.h:401 Bool isKindOf(KindOfType t) const { return TEST_KINDOFMASK(m_kindof, t); }
The flag enum lives in KindOf.h. The important discriminators for this article sit early in the file:
KINDOF_STRUCTURE— a building (line 52)KINDOF_IMMOBILE— never moves (line 47)KINDOF_INFANTRY— soldier (line 53)KINDOF_VEHICLE— tank, jeep, hovercraft (line 54)KINDOF_AIRCRAFT— plane or helicopter (line 55)KINDOF_MP_COUNT_FOR_VICTORY— losing all of these loses the game (line 84)
Gameplay systems ask isKindOf constantly. The AI, the pathfinder, the selection code, score-keeping, the victory check — none of them look at class types. They look at bits.
Structures
A typical production structure sets STRUCTURE, IMMOBILE, and MP_COUNT_FOR_VICTORY, attaches an ActiveBody (structures use ActiveBody too, not a special StructureBody class — the name is historical), and omits any AIUpdate module. What makes it a building is the absence of a locomotor set and the presence of structure-centric modules like ProductionUpdate, GarrisonContain, ProductionSpecialPowerModule, or PowerPlantUpdate.
Object AmericaPowerPlant
KindOf = STRUCTURE IMMOBILE SELECTABLE CAN_ATTACK MP_COUNT_FOR_VICTORY
SCORE CAPTURABLE FS_POWER AUTO_RALLYPOINT
Body = ActiveBody ModuleTag_05
MaxHealth = 500.0
End
Behavior = PowerPlantUpdate ModuleTag_07
End
Behavior = StructureCollapseUpdate ModuleTag_09
MinCollapseDelay = 000
MaxCollapseDelay = 000
End
End
No AIUpdate, no Locomotor. The pathfinder will never ask it to move.
Units
A unit replaces those structural modules with an AIUpdateInterface (or a subclass — DozerAIUpdate, TransportAIUpdate, JetAIUpdate) and picks a locomotor set. A Ranger looks roughly like this:
Object AmericaInfantryRanger
KindOf = PRELOAD SELECTABLE CAN_ATTACK INFANTRY SCORE CAN_RAPPEL
PARACHUTABLE ATTACK_NEEDS_LINE_OF_SIGHT
Body = ActiveBody ModuleTag_04
MaxHealth = 120.0
End
Behavior = AIUpdateInterface ModuleTag_05
AutoAcquireEnemiesWhenIdle = Yes
End
Locomotor = SET_NORMAL HumanLocomotor
End
The VEHICLE/INFANTRY/AIRCRAFT bit branches downstream: the pathfinder picks a different layer, the UI picks a different cursor, the AI picks a different target-priority table, and formation movement applies a different spacing rule. Infantry additionally unlocks CAN_RAPPEL and PARACHUTABLE paths that vehicles can't use. Aircraft takes over the flight layer entirely.
Production and construction
A structure "produces" a unit through a ProductionUpdate module that maintains a per-structure queue. When a job finishes, the module calls TheThingFactory->newObject with the queued template and places the result at the structure's rally point. The unit is born as a full Object — the factory structure doesn't know or care what it just built, and the produced unit doesn't carry a reference back.
Construction works the same way in reverse. A building under construction is itself an Object, stamped from the same template as the finished building but with an intermediate behavior module (StructureBody-derived construction updaters) throttling health and disabling production until the construction progress completes. See The Object Model for the template pipeline and Modules and Behaviors for the module system itself.
Quirks
- Walls and power plants don't count for defeat. The victory check at VictoryConditions.cpp:351 iterates only objects with
KINDOF_MP_COUNT_FOR_VICTORY. Power plants, walls, and upgrade structures are intentionally excluded. You can have a base of nothing but power plants and still technically be alive. IMMOBILEis what blocks pathfinding cells, notSTRUCTURE. A prop or tree that isIMMOBILEwithoutSTRUCTUREstill reserves cells. A theoretical mobile structure would not.- An object can hold both
VEHICLEandSTRUCTURE. The engine allows it and a few gantry-type objects use the combo. Code that assumes the flags are exclusive will misclassify these — check explicitly for what you want. - Aircraft use the pathfinder's flight layer, not terrain.
KINDOF_AIRCRAFTbypasses terrain obstacles but still routes through the pathfinder for collision and formation. - Infantry takes
SET_NORMALwith aHumanLocomotor, aircraft alternatesSET_TAXIINGandSET_NORMAL, structures have no locomotor set at all. The absence of a set is the engine's real "is this thing mobile" check — more reliable than reading kindof bits. - Construction placement uses
IMMOBILEto decide blocked cells, which is why correct cliff-cell classification matters when a dozer tries to put a building near a slope — see Cliff Cells. - Template inheritance is handled by
Overridable, not by the INI parser.AmericaInfantryPathfinderinherits the base infantry template and overrides a few fields; both blocks live in INI and the engine merges them at load time. A mod that adds a new Object block can do the same.