I'm making a MUD game engine that supports D&D , Pathfinder and other tabletop-RPG rule sets. I am starting to build out how characters interact with the world (e.g. dialog, battle, searching for traps).
The engine is pretty loosely coupled, to facilitate swapping out components with downloaded plugins and I want to continue down that path with the rules. I've got an ICharacter
interface and both a player and non-playable character interface that inherits from ICharacter
. I feel that making a developer re-implement those interfaces when making a plugin because the developer is building a Pathfinder plugin instead of a D&D plugin is poor design. Instead, I want to allow them to specify at runtime (via an editor) what rules each character must abide by when performing specific tasks.
I was thinking I'd have a RuleEngine
that would be provided an object and process a rule. Some pseudo code for this:
public void Attack(ICharacter target)
{
IRuleResult result = RuleEngine.Process(
RuleFactory.CreateRule("CharacterAttack"),
this);
// Check result to see what was given back like damage, hit/miss etc.
}
The user within my editor can specify that for CharacterAttack
rule requests, uses PathfinderAttackRule
, which would be a Type implementing IRule
.
This approach feels like it might be to restrictive though. The interface would have to have some knowledge of D&D implementations, like critical miss, damage amounts etc. What if the user wants to build something that isn't D&D, but just a custom combat rule? The interface doesn't allow it and the engine would expect the interface properties to be usable.
Another option I was thinking about was having delegates perform the rule checks and process the attack.
public void Attack(ICharacter target)
{
Func<ICharacter, IPlayer, bool> attackCheck =
RuleFactory.CreateRule<IPlayer>("PreAttach");
if (attachCheck(target, this))
{
// Invokes a delegate fetched by an internal factory.
RuleFactory.Process<IPlayer>("AttachPlayer");
}
}
This lets builders specify within the editor what custom delegates to use before an attack starts. (it would be a dropdown list of delegates supported, scanned from the assemblies loaded). The user might want a D&D rule delegate from a plugin they downloaded for pre-attack rule checks, then use a pathfinder rule for the actual attack from a 2nd plugin they downloaded.
Now that I've explained what I'm wanting to do, am I way off base here? Are both of the above examples horribly thought-out and/or is there a better approach to take that is loosely coupled and allows swapping rules out?
As a side-note, I chose player combat as the example, but rules are applied to more than just combat, and more than just players. Rooms, items and other objects will have rules. Some of the rules can be shared, some can not.
Where should I be going with this? I'm looking for general concepts and best-practices.
Strategy
pattern. \$\endgroup\$