If you now think that the User Interface and Triggered abilities can share the event handling system, therefore effectively reducing the ammount of programming needed for each of them, I fear you'll be disappointed. The reason for this is the undo system. While it enables things like handling illegal actions, the most direct interpretation of what it does prevents me from sharing one event system: Undo means that every action and event can happen in two directions in time.
When you attack with a creature, it becomes tapped. An ability could trigger from that tapping. If you now determine that the creature isn't allowed to attack, everything's undone and the creature is untapped again. You want to see the creature untapped in the user interface, but you don't want abilities trigger from it untapping.
Of course the event handling functions after the same principle, but the events are duplicated - in some sense. There's no real 1:1 mapping between GUI and game events, because the GUI is satisfied by state-changes, while the game needs more high level events: The gui doesn't care if you've drawn a card or just put it into your hand. It just needs to know that your library has become smaller and your hand grew larger.
Fortunately, Java has some built in functionality for handling these low-level events, namely PropertyChangeSupport. It allows you to keep track of properties and notify the listeners whenever a change occurs. Well, you still have to call the listeners on a change, but the major work, managing all the listeners and what properties they are interested in, is done for you.
Along with adding PropertyChangeSupport to Laterna Magica, I have restructured a lot of code. Previously, every class implemented undo support for its properties itself. This is now separated into an extra class, which also allows to use PropertyChangeSupport more easily:
public class EditableProperty
private EditablePropertyChangeSupport s;
private String name;
private T value;
public EditableProperty(Game game, EditablePropertyChangeSupport s, String name) {
this(game, s, name, null);
}
public EditableProperty(Game game, EditablePropertyChangeSupport s, String name, T initialValue) {
super(game);
this.s = s;
this.name = name;
value = initialValue;
}
public void setValue(T value) {
new SetValueEdit(value).execute();
}
public T getValue() {
return value;
}
@Override
public String toString() {
return valueOf(getValue());
}
private class SetValueEdit extends Edit {
private static final long serialVersionUID = 93955529563844615L;
private T oldValue, newValue;
public SetValueEdit(T newValue) {
super(EditableProperty.this.getGame());
this.newValue = newValue;
}
@Override
protected void execute() {
oldValue = value;
value = newValue;
if(s != null) s.firePropertyChange(name, oldValue, newValue);
}
@Override
protected void rollback() {
value = oldValue;
if(s != null) s.firePropertyChange(name, newValue, oldValue);
}
@Override
public String toString() {
return "Set " + s.getSourceBean() + "'s " + name + " to " + newValue;
}
}
}