I'm currently working on a clone of an old turn-based strategy game "Stars!" and am looking for solutions to a challenge in game engine implementation.
The premise of the game is that every player takes control over the entire race of his denizens (called "colonists") and fight other players to conquer the known part of the galaxy. Each player can design their race as they please. The first choices presented when creating a race are those of the Primary Racial Trait (PRT - must select exactly one from a list of 10) and Lesser Racial Traits (LRT - can select any number of them from a list of 14). A PRT usually modifies several aspects of the base game, e.g. sets starting technology level, gives some starting spaceships, controls the availability of certain ship parts and modifies the rules of the game for that player while LRTs provide minor advantages/disadvantages.
A few examples to show the extent of game modifications as well as interactions between them:
- PRTs can modify the same part of the rules from two directions: Inner Strength PRT - 10% increased strength of defending colonists; War Monger PRT - 10% increased strength of attacking colonists.
- PRT and LRT can directly modify the same "property" of a race: Jack Of All Trades PRT - +20% maximum planet population; Only Basic Remote Mining LRT - +10% maximum planet population; they cumulate multiplicatively (+32%).
- LRTs can work conditionally based on other LRTs: Improved Fuel Efficiency grants access to several unique types of engine, where one of them is granted only if another LRT No Ram-Scoop Engines is not chosen.
- PRT can fundamentally modify the game mechanics: Alternate Reality PRT races do not live on planets but rather in orbiting starbases. Rules of economy (resource production) are completely different for them as they do not build planetary facilities (factories and mines).
Constraints - the challenge
- Make it possible to design new PRTs and LRTs and put them in the game without much pain.
- (Induced from previous) They must be in some way decoupled from the main engine (so the core "rules engine" cannot be based on if/switch statements that change flow based on PRT/LRT).
What design patterns would be suitable to this problem of "pluggable modifiers"?
The language is C#, if that helps with suggesting more specific solutions. So far my brain is wandering in the realm of attaching the Traits to events in the engine where it calculates certain properties (so they could modify the output of these calculations), but I don't want to suggest too much.