Join GitHub today
GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.Sign up
There are n main sections to OpenRA: UI, Rendering, unit behaviour, ...
All units/structures/most things in the map are Actors. Actors contain a collection of traits. Traits consist of an info class and a class that does stuff. There is one instance of the infoclass shared across all actors of the same type. Each actor gets its own instance of the trait class itself. Infoclasses are responsible for instantiating their corresponding trait class -- see ITraitInfo, and TraitInfo for the trivial implementation of this. In some cases the trait class's constructor needs some args, in which case TraitInfo can't be used. This is a limitation of C# generics.
Actor assembly is done via the mod's yaml files. A section exists for each actor type, and within that section we list the traits the actor should have. These get looked up in the loaded mod DLLs. Each trait can contain properties, which are automatically loaded into the corresponding fields on the trait's ITraitInfo.
Traits: look at TraitsInterfaces.cs We've tried to make individual traits implement as self-contained a unit of functionality as possible - all cross-trait references should be in terms of an interface from TraitsInterfaces.cs.
Things an actor can be doing are represented as Activity subclasses. Actor has a queue of these. The standard set of activities are in OpenRA.Mods.Common/Activities. Ground vehicle movement is more complex, and has bits in OpenRA.Mods.Common/Move. Aircraft use different bits again, in OpenRA.Mods.Common/Air. There are some bits elsewhere for custom infantry behaviors, etc. In some cases, a trait or activity will maintain an internal subqueue of activities. This works exactly the same way as the actor's main activity queue -- its state is evolved by Util.RunActivity().
Units offer orders they can perform (given context) through traits that implement IIssueOrder. There are two parts to this -- an Orders collection, which exposes IOrderTargeter objects, which describe orders that the actor could generate, and the rules for choosing which to perform when the "default action" button is pressed (RMB). The second part is IssueOrder() itself, which resolves an IOrderTargeter into an actual order object to be sent.
For more complex things that require modal UI (like special abilities, RA-style sell/repair buttons, etc) we have IOrderGenerator implementations. This can completely replace the normal actors-provide-orders model temporarily. IOGs wiring is provided through OpenRa.Game/Controller.cs (ToggleInputMode, CancelInputMode)
Things that don't affect gameplay, or (increasingly) are just transient are implemented as IEffect, rather than real Actors. This is similar to the temp ents mechanism in many other game engines.
Most player-level or global-level game behavior is implemented as traits on special Player and World actors. These are accessible via Player.PlayerActor and World.WorldActor. This includes production queue support, ore/tiberium growth, various palette manipulation magic.
Many traits can be modified by adding an appropriate IFooModifier implementation to the unit. This includes rendering, where IRenderModifier allows you to define an arbitrary transform on the Renderables emitted by the actor's IRender implementation(s). Examples are things like cloaking, invisibility to certain players, flying units with shadows, etc. Other modifiers can affect movement speed, damage taken, weapon firepower, etc.
Game code is collected into "Mod" units. Mods can be added prior to starting the game. Currently there is no dependancy mechanism, but provided you are doing additions or overrides you can add multiple mods without problem. Everything is a mod (including RA - which is loaded by default).
The contents of the mod is defined in a manifest file mod.yaml. This lists the packages containing art assets (typically .mix files), yaml files defining actor definitions.
The unit artwork itself must be defined in one or more sequences files. These are specified in the manifest. RA puts everything in mods/ra/sequences.yaml. check mod.yaml for a list of what the mod uses); the format is self explanatory.
Other things we probably want to put in here:
- A guide on how to add a generic unit via yaml using existing traits (and then introduce some element that requires a simple trait change).
- how to set up a new mod (TC-style or mutator-style)
- VFS (OpenRa.FileFormats.FileSystem, Package, Folder classes)
- Trait inheritance (and the magicness of ^ActorType)
- Removing inherited traits (prepend
-to the trait name)
- Multiple instances of a trait (
@and all subsequent characters are ignored for the purposes of looking up the trait).