It is currently 27 Apr 2024, 22:55
   
Text Size

Why use an event system

General Discussion of the Intricacies

Moderator: CCGHQ Admins

Why use an event system

Postby Incantus » 23 Jan 2009, 19:04

Here's the best justification for using an event system:

When an event happens in magic, other things might care about the event, but the cause of the event doesn't care who responded to that event. Which suggests that you should completely decouple the objects generating the events from the objects listening for them. A general publish/subscribe event system gives you this decoupling. This will make your design much simpler.

For example, in Incantus all important objects derive from a common MtGObject, who's sole behavior is to send and listen to events. Almost everything derives from this, including Player, Cards, the Abilities hierarchy, since they all have to send events (arguably this is a stupid design, since I'm already using a global event dispatching system, so anybody object could access it anyway, but it was the first design decision I made so I'm stuck with it).

For example, these are the events currently sent in Incantus:
Code: Select all
class GameStartEvent(Event): pass
class GameOverEvent(Event): pass
class HasPriorityEvent(Event): pass
class TimestepEvent(Event): pass
class GameFocusEvent(Event): pass
class LogEvent(Event): pass

class LifeGainedEvent(Event): pass
class LifeLostEvent(Event): pass
class DrawCardEvent(Event): pass
class DiscardCardEvent(Event): pass
class ShuffleEvent(Event): pass
class CardEnteringZoneFrom(Event): pass
class CardEnteredZone(Event): pass
class CardLeftZone(Event): pass
class CardCeasesToExist(Event): pass
class ControllerChanged(Event): pass
class TokenLeavingPlay(Event): pass
class CounterAddedEvent(Event): pass
class CounterRemovedEvent(Event): pass
class MorphEvent(Event): pass
class ClashEvent(Event): pass
class CardCycledEvent(Event): pass

class NameModifiedEvent(Event): pass
class CostModifiedEvent(Event): pass
class TextModifiedEvent(Event): pass
class TypesModifiedEvent(Event): pass
class ColorModifiedEvent(Event): pass
class SubtypesModifiedEvent(Event): pass
class SupertypesModifiedEvent(Event): pass
class AbilitiesModifiedEvent(Event): pass
class PowerToughnessModifiedEvent(Event): pass
class LoyaltyModifiedEvent(Event): pass

class ManaAdded(Event): pass
class ManaSpent(Event): pass
class ManaCleared(Event): pass
class CardTapped(Event): pass
class CardUntapped(Event): pass

class LandPlayedEvent(Event): pass
class AbilityAnnounced(Event): pass
class AbilityCanceled(Event): pass
class AbilityPlacedOnStack(Event): pass
class AbilityRemovedFromStack(Event): pass
class AbilityResolved(Event): pass
class AbilityCountered(Event): pass
class AbilityPlayedEvent(Event): pass
class SpellPlayedEvent(Event): pass
class DeclareAttackersEvent(Event): pass
class DeclareBlockersEvent(Event): pass

class AttackerSelectedEvent(Event): pass
class AttackersResetEvent(Event): pass
class BlockerSelectedEvent(Event): pass
class BlockersResetEvent(Event): pass
class AttackerDeclaredEvent(Event): pass
class BlockerDeclaredEvent(Event): pass
class AttackerBlockedEvent(Event): pass
class AttackerClearedEvent(Event): pass
class BlockerClearedEvent(Event): pass
class CreatureInCombatEvent(Event): pass
class CreatureCombatClearedEvent(Event): pass
class RegenerateEvent(Event): pass
class DamagePreventedEvent(Event): pass
class DealsDamageEvent(Event): pass
class DealsDamageToEvent(Event): pass
class ReceivesDamageEvent(Event): pass
class PermanentSacrificedEvent(Event): pass
class PermanentDestroyedEvent(Event): pass
class AttachedEvent(Event): pass
class UnAttachedEvent(Event): pass
class CardSelectedEvent(Event): pass
class AllDeselectedEvent(Event): pass
class TargetedByEvent(Event): pass
class InvalidTargetEvent(Event): pass

class NewTurnEvent(Event): pass
class TurnFinishedEvent(Event): pass
class GameStepEvent(Event): pass
class UntapStepEvent(GameStepEvent): pass
class UpkeepStepEvent(GameStepEvent): pass
class DrawStepEvent(GameStepEvent): pass
class MainPhase1Event(GameStepEvent): pass
class MainPhase2Event(GameStepEvent): pass
class EndMainPhaseEvent(GameStepEvent): pass
class BeginCombatEvent(GameStepEvent): pass
class AttackStepEvent(GameStepEvent): pass
class BlockStepEvent(GameStepEvent): pass
class AssignDamageEvent(GameStepEvent): pass
class EndCombatEvent(GameStepEvent): pass
class EndTurnStepEvent(GameStepEvent): pass
class CleanupPhase(GameStepEvent): pass
class CleanupEvent(GameStepEvent): pass
Some of these events are also listened to by the UI, which makes it fairly easy to decouple the UI from the game engine.
Incantus
DEVELOPER
 
Posts: 267
Joined: 29 May 2008, 15:53
Has thanked: 0 time
Been thanked: 3 times

Re: Why use an event system

Postby frwololo » 23 Jan 2009, 22:48

Isn't there a risk of infinite loop with this design ?
frwololo
DEVELOPER
 
Posts: 265
Joined: 21 Jun 2008, 04:33
Has thanked: 0 time
Been thanked: 3 times

Re: Why use an event system

Postby Incantus » 24 Jan 2009, 00:13

There is, but that risk is also considered in the comprehensive rules. If you have an infinite loop, then the game becomes a draw. It's just a matter of tracing execution paths.
Incantus
DEVELOPER
 
Posts: 267
Joined: 29 May 2008, 15:53
Has thanked: 0 time
Been thanked: 3 times

Re: Why use an event system

Postby MageKing17 » 25 Jan 2009, 02:30

The thing about infinite loops is that the cards themselves are usually designed in such a way as to prevent this from happening. For example, consider a card with the text "When ~ comes into play, remove all permanents from the game" and "When ~ leaves play, return the removed permanents to play under their owners' control". No card is ever printed with both of these abilities, and for a very good reason... playing this card leads to an infinite loop (in real Magic, this will cause the game to be a draw). Nonetheless, if your implementation of the game doesn't allow such an occurrence, your implementation is incomplete... you won't have to worry about such a card wrecking the game because Wizards wouldn't print it, but you shouldn't take it upon yourself to prevent it from ever happening (by not adopting an event system that can let it happen), because the rules say it can.

So, in short, yes, it allows infinite loops. But that's intended behavior.
User avatar
MageKing17
Programmer
 
Posts: 473
Joined: 12 Jun 2008, 20:40
Has thanked: 5 times
Been thanked: 9 times

Re: Why use an event system

Postby mtgrares » 27 Jan 2009, 20:34

That is alot of events, lol. My favorite is NameModifiedEvent, I'm sure that a card can have it's name changed, but I can't cite an example off of the top of my head.
mtgrares
DEVELOPER
 
Posts: 1352
Joined: 08 Sep 2008, 22:10
Has thanked: 3 times
Been thanked: 12 times

Re: Why use an event system

Postby MageKing17 » 28 Jan 2009, 02:47

mtgrares wrote:That is alot of events, lol. My favorite is NameModifiedEvent, I'm sure that a card can have it's name changed, but I can't cite an example off of the top of my head.
Copy effects copy names. :P
User avatar
MageKing17
Programmer
 
Posts: 473
Joined: 12 Jun 2008, 20:40
Has thanked: 5 times
Been thanked: 9 times

Re: Why use an event system

Postby mtgrares » 28 Jan 2009, 19:51

I tend to make events that are "too large". Like a zoneEvent will includes both adding and removing cards, while I should probably split them into zoneAddEvent and zoneRemoveEvent. I think I need to make the events "more fine grained."

Instead of
Code: Select all
zone.addEvent(Event)
I should have
Code: Select all
zone.addEvent_Add(Event)
zone.addEvent_Remove(Event)
mtgrares
DEVELOPER
 
Posts: 1352
Joined: 08 Sep 2008, 22:10
Has thanked: 3 times
Been thanked: 12 times


Return to Magic Rules Engine Programming

Who is online

Users browsing this forum: No registered users and 10 guests


Who is online

In total there are 10 users online :: 0 registered, 0 hidden and 10 guests (based on users active over the past 10 minutes)
Most users ever online was 4143 on 23 Jan 2024, 08:21

Users browsing this forum: No registered users and 10 guests

Login Form