1
\$\begingroup\$

When you play card games, you always face some abilities and effects will be triggered at the same time. To resolve this, players relies on the rules. For example, if a minion dies, it triggers effects on my side and opponent side. If the effects is handled on my side first if it is my turn.

But how would you resolve it in computer games. Even you are using mediator for event handling, the event is usually handled by the order of subscription. Unless there are some indicators for the subscribers, you will not able to distinguish between the subscribers. But wouldn't it defeats the purpose of observer pattern? What is the best way implement it?

\$\endgroup\$
2
  • \$\begingroup\$ Are you asking how to decide what event should be handled first or how to handle two events as if they occurred simultaneously (at the same time)? \$\endgroup\$
    – Charanor
    Jan 16 '17 at 10:58
  • \$\begingroup\$ @Charanor Not really. I am asking if there are multiple listeners to one event. How can I handle the ordering? \$\endgroup\$
    – Joshua
    Jan 16 '17 at 11:01
0
\$\begingroup\$

Pending a clarifying edit to your original question, I assume the following:

  1. The game implements the observer behavioral design pattern using some baseTableObserver class from which Player inherits.

  2. Some TableTop class exists that subscribes, unsubscribes, and notifies its vector of TableObserver references.

  3. Each Player submits actions to an instance of TableTop for processing in turns, for example table.SubmitAction( playerOne, action );

  4. The TableTop instance resolves all submitted actions after a round of Player action submissions.

Wherein table.SubmitAction( playerOne, action );:

  • table is an instance of class TableTop which is either globally visible, or that each player has a reference to.
  • SubmitAction is a public member function of class TableTop
  • playerOne is an instance of class Player (which inherits from TableObserver)
  • action is either an instance of class PlayerAction, or some other arbitrary argument such as a std::string like "floop the pig"

Option 1

Start a globally visible timer at the beginning of the game that continuously counts up using an unsigned 32-bit value.

Then, either modify TableTop's SubmitAction function to take an additional game-time-stamp table.SubmitAction( playerOne, action, gameTime );, or have TableTop internally cache the game-time during SubmitAction execution.

After the TableTop instance processes all the submitted actions for a round of play, it can sort its list of TableObserver references in order of submission time-stamps. Finally, the TableTop can iterate over the newly sorted list of TableObserver references.


The same time-stamping idea could also be applied during the TableObserver notification process:

Either TableTop can send the game-time-stamp as observer.notify(notificationTime), or TableObserver can internally cache the game-time during notify execution. Both require that the TableObserver class have an unsigned 32-bit notifiedTime variable.

A Player can then use notifiedTime for whatever duration/delay calculations it may need.


Option 2

Forget the round of play before resolution mechanic, and replace the overly-specific base observer class TableObserver with the general Observer.

Assume there is a MinionCard class, that inherits from aCard class which can subscribe, unsubscribe, and notify instances of Observer (for example a Player), as before.

If an instance of MinionCard has its health drop below 0, then -- as-per your question's initial example -- it would need to notify the Player whose turn it currently is, then any other players that event may affect.

This can be accomplished by giving the base Observer class an internal priority value, which is updated either each turn, or updated based on some other rule such as physical proximity of Player instances.

For example, the Player whose turn it is always has priority == 1, and the remaining Players' priorities are calculated by the square of the distance between the Player with priority == 1, hence increasing with range.

This priority updating can either be handled by each Player instance internally, or more simply by a PlayerManager class or some other such object that tracks all the players on a moment-to-moment basis.

Now, going back to MinionCard's death, before it begins it iteration over its vector of Observer references, it can sort that vector based on Observer priority.

For example, using C++'s STL:

std::sort( observerList.begin(), 
           observerList.end(), 
           [] (auto && a, auto && b) { 
               return a.priority < b.priority; 
           } 
);
for ( Observer & observer : observerList ) {
    observer.notify();
}

The overall idea is to sort the Observer references before notifying them. I hope that helps. Again, SE is more useful if questions are as specific as possible.

\$\endgroup\$
-1
\$\begingroup\$

I would use global counter or timestamp on event delivery and process them by this ordered.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.