I’ve finished the refactoring of the Events and Actions system.  I’m quite happy with the results; it removes the need for derived Event objects, works for Items and (when they’re in place :)) Skills, Buffs, and CellEffects.  It’s still hookable so that any event can be filtered or modified.  As an example, here’s the Item that the Player starts with for now:

    <Weapon Id="Axe1" Name="Rusty Hatchet" Subclass="Axe" WeaponPower="10" ItemLevel="1" Level="1-3"
            Tile="AxeBeaked" Description="A rusty hatchet">
      <Event Type="MeleeAttackHit">
        <MagicMapping Amount="10%"/>
        <Heal Amount="1%" Target="Self" />

When a MeleeAttack hits, the above actions are triggered automatically. The cool thing is that those same Actions can be triggered in a Skill by a SkillUsed event or by a Cell by a CellEntered event.

Here’s the list of currently hookable events; as new Actions come online, they’ll get added as well:

    public enum EventType
        MeleeAttackStarted, MeleeAttackMissed, MeleeAttackDodged, MeleeAttackBlocked, MeleeAttackHit,
        DidDamageToActor, DamageDoneByActor,
        EnteringCell, ExitingCell,
        AddingBuff, BuffAdded,
        StartMagicMapping, DoneMagicMapping,
        StartHealing, DoneHealing,
        StartTeleporting, DoneTeleporting,
        OpeningDoor, OpenedDoor,
        StartResting, DoneResting,

The hooking opportunity itself is provided by the Actions themselves. For instance, here’s the code for an OpenDoor Action:

    public class Action_OpenDoor : Action
        internal static Action_OpenDoor Create(Actor actor, Direction nextDir)
            // Obtain an Action from the object pool (or if none are in it, alloc a new one).
            Action_OpenDoor action = ObjectPool.IsEmpty ? new Action_OpenDoor() : ObjectPool.GetFreeObject();

            // Initialize fields in the newly obtained Action.
            action.Info.PerformingObject = actor;
            action.Info.Direction = nextDir;

            return action;

        internal override void Release()

        public override void Perform(ActionInfo actionInfo)
            Actor movingActor = actionInfo.PerformingActor;

            // Notify the system that we're performing this Action; this allows other things to hook into it and modify/filter the event
            if (!FireEvent(actionInfo, EventType.OpeningDoor))
            // Get a pointer to the neighboring Door Cell's state, and set it to open
            Cell_Door doorCell = movingActor.Location.Neighbor(actionInfo.Direction)  as Cell_Door;

            FireEvent(actionInfo, EventType.OpenedDoor);

        static ObjectPool ObjectPool = new ObjectPool();

So in the above example, a Map could opt to – for whatever reason – disallow Events of type OpeningDoor, and filter out the event above.

Updated source: http://wanderlinggames.com/files/dungeon/dungeon-3-29-11.zip

Updated executable: http://wanderlinggames.com/files/dungeon/dungeonexe-3-29-11.zip