I find that the command pattern is one of the most effective behavioral design patterns, it is one of the most effective ways to add dynamic behavior to a game, i use it in pretty much every single one of my games. This “Verb” system is simply a variation on the command pattern.
Here is a c# code example of an action base class and a moveUpAction from one of my projects
public delegate void ActionDelegateForAction(Creature owner, Creature interactor, Engine engine, MapChunk chunk);
[Serializable]
public class Action
{
public List<Delegate> actionList;
public float energyCost = 50.0f;
public Action()
{
actionList = new List<Delegate>();
ActionDelegateForAction d = new ActionDelegateForAction(doGenericAction);
actionList.Add(d);
}
private void doGenericAction(Creature owner, Creature interactor, Engine engine, MapChunk chunk)
{
Console.WriteLine("called generic action");
}
public void doAction(Creature owner, Creature interactor, Engine engine, MapChunk chunk)
{
actionList[0].DynamicInvoke(owner, interactor, engine, chunk);
owner.addEnergy(-energyCost);
}
}
[Serializable]
public class MoveUpAction : Action
{
public MoveUpAction()
{
actionList = new List<Delegate>();
ActionDelegateForAction d = new ActionDelegateForAction(doGenericAction);
actionList.Add(d);
}
private void doGenericAction(Creature owner, Creature interactor, Engine engine, MapChunk chunk)
{
if (!chunk.tileMap[owner.getX(), owner.getY() - 1, owner.getZ()].getBlocks())
{
owner.setY(owner.getY() - 1);
owner.setLastDirection(Direction.North);
owner.setMovementState(movementState.walk);
if (owner.getIsPlayer())
{
engine.PlayBlockedSoundEffect("data/sound/sfx/footstepLibrary.wav", true, 50);
}
}
else
{
Creature creature = engine.getCreatureByPosition(owner.getX(), owner.getY() - 1, owner.getZ());
if (creature != null && creature.getInteractable() != null)
{
owner.setLastDirection(Direction.North);
creature.getInteractable().doAction("action1", creature, owner, engine, chunk);
owner.setMovementState(movementState.walk);
}
}
}
}
And here’s another more complex example, a differnet action delegate class
public delegate void ActionDelegate(Creature owner, Creature interactor, Engine engine, MapChunk chunk);
[Serializable]
public class Interactable
{
public Dictionary<string, string> phraseList;
[NonSerialized]
public Dictionary<string, Delegate> actionList;
public Interactable()
{
initializeInteractable();
}
public virtual void initializeInteractable()
{
actionList = new Dictionary<string, Delegate>();
phraseList = new Dictionary<string, string>();
ActionDelegate d = new ActionDelegate(doGenericAction);
actionList.Add("actionOne", d);
phraseList.Add("actionOne", "Do Generic Action");
}
private void doGenericAction(Creature owner, Creature interactor, Engine engine, MapChunk chunk)
{
Console.WriteLine("called generic interactable");
}
public void doAction(string action, Creature owner, Creature interactor, Engine engine, MapChunk chunk)
{
if (actionList == null)
{
initializeInteractable();
}
if (actionList.ContainsKey(action))
{
actionList[action].DynamicInvoke(owner, interactor, engine, chunk);
}
}
public virtual bool getHasAction(string action)
{
return actionList.ContainsKey(action);
}
public virtual string getActionPhrase(string action)
{
if (phraseList.ContainsKey(action))
{
return phraseList[action];
}
return null;
}
public virtual void setAction(string action, string description, ActionDelegate functionAction)
{
if (actionList.ContainsKey(action))
{
actionList[action] = functionAction;
}
else
{
actionList.Add(action, functionAction);
}
if (phraseList.ContainsKey(action))
{
phraseList[action] = description;
}
else
{
phraseList.Add(action, description);
}
}
public virtual void removeAction(string action)
{
if (actionList.ContainsKey(action))
{
actionList.Remove(action);
}
if (phraseList.ContainsKey(action))
{
phraseList.Remove(action);
}
}
public virtual void setActionPhrase(string phrase, string action)
{
if (phraseList.ContainsKey(action))
{
phraseList[action] = phrase;
}
}
}
[Serializable]
public class InteractibleDoor : Interactable
{
public InteractibleDoor()
{
initializeInteractable();
}
public override void initializeInteractable()
{
actionList = new Dictionary<string, Delegate>();
phraseList = new Dictionary<string, string>();
ActionDelegate d = new ActionDelegate(openDoor);
setAction("action1", "Open Door", d);
}
private void openDoor(Creature owner, Creature interactor, Engine engine, MapChunk chunk)
{
//alter sound effect based on distance to player
engine.playSpatialSoundEffect("data/sound/sfx/door.wav", true, owner.getX(), owner.getY(), owner.getZ(), 100, 1);
owner.setTileNum('|');
chunk.setNotBlocked(owner.getX(), owner.getY(), owner.getZ(), false);
chunk.setNotBlocksLight(owner.getX(), owner.getY(), owner.getZ(), true);
removeAction("action1");
ActionDelegate d = new ActionDelegate(closeDoor);
setAction("action1", "Close Door", d);
}
private void closeDoor(Creature owner, Creature interactor, Engine engine, MapChunk chunk)
{
owner.setTileNum((char)20);
chunk.setBlocked(owner.getX(), owner.getY(), owner.getZ(), false);
chunk.setBlocksLight(owner.getX(), owner.getY(), owner.getZ(), true);
removeAction("action1");
}
}
And here is code utilizing the first action pattern class i showed
hitting move up and move left at the same time lets me move diagnolly. We need to use some kind of action system, that atomizes, for example, the bite and move actions, and we can loop through the list of Actions (you use the base class) and run their doAction method in c# as long as doAction is a virtual method, you dont even need to know the type when you run doAction on the base class it will do the child classes variation of it. You use this create a list of actions based on a list of strings and loop through and run doAction, to create complex actions, that the player can define.
here is some example possible code:
The key is, I can also do:
Action[] theActionList = {new EjectAgent(“ocytoxyNT”), new MoveUpAction())
foreach Action a in theActionList {
a.doAction(player,player,this.mapChunk)
}