Comments but also some more questions:
I think it makes it easier to develop if the Game isn't working with a wide variety of Event objects. You can make Events be the product of composition instead of inheritance by passing an array of objects that would represent the construction parameters. Usually the first element would need to be an enum or flag to indicate the type of the event.
However, in the languages I'm using (C++), an array must all be of a single type, yet depending on the kind of event we may need parameters of multiple types. E.g. a message event may take in both a message -- a string -- and also a color -- a number -- to display it as. So a single array would not work. A "ChangeLevel" event (signals that we are transitioning to a different level map) would have to pass a reference/pointer/handle/etc. to the new level map -- yet another type (though a handle might still just be a number). So we have strings, numbers, pointers to level maps. That's 3 different types right there. An "explosion" event might need a reference or handle for the asset of the explosion, PLUS a coordinate indicating where it happens. So there is 2 types in the same event: reference/handle to asset and a coordinate (which in my program is a single type and not just two numbers separate from each other).
What do you suggest should be done in this case?
Prompts are pure UI. They're simply a menuing schema to help the player communicate to the game what s/he wants to do. Or rather, prompts/menus help the user fill out the parameters necessary to produce a game action that makes sense. In this vein, I don't use callbacks. The Game propagates an event saying what type of situation the player is in and what range of actions are available. The UI is responsible for formatting that information in a way that helps the user select an action and send it to the game. I just don't see where there is a need for a callback (in this case). Even if the action being taken doesn't have to do with the scheduling of the game, it's still a 'wait for player action' scenario, so, again, as long as the UI knows which actions to present to the player, you don't need anything fancy. I don't see any harm in using a callback though.
Why callbacks? Well, the answer was discussed earlier in this thread. We have a main loop (this is not the game logic loop, but the main program loop), which fetches the input and passes it to the window manager:
Loop until (stopping condition):
input = inputSource.grabInputEvent();
UI.handleInputEvent(input);
UI.render();
or something like that. So there's a simple sequence input -> logic -> render involved. The input goes into the window manager and then to the pane that's focused. Note that the above loop is sort of akin to a Windows message loop in a Windows program.
The reason for the callbacks is to keep things so that all input-getting and render is handled by that loop. Suppose we press a key to do something in the game, and the logic says it needs a confirmation prompt. The event goes out to the UI object (or the Application, but gets there somehow), and the UI processes it (this all happens when the logic called the receiveUIEvent() thingy). So the UI does something, like put out a pane showing the confirmation prompt. But now in order for input to hit that confirmation prompt, control has to return to the outer, main loop just mentioned. So our logic function must stop there and let control bubble back up to the main loop where the input is gotten and passed on to the UI to handle the prompt. Which means once the prompt is satisfied, the logic must resume, and the callback handles this by calling the logic function that handles everything post-prompt.
The other way I mentioned, which would avoid a callback, would require the UI to spawn a new "pump" (like the main loop just mentioned) when it receives an event, to handle this, creating a blocking call when such a UI event is sent. But it seems this would break the centralization of the getting of input, the logic, and the render and we'd end up ultimately starting a UI pump inside a UI pump.
Or do you know of a third option? If so, what is it? Also, if you use a non-callback option, how do you get the return values from the prompts (whether yes or no was pressed, for example)?
I think that kind of depends. Suppose you send an event for creature that moves in the player's LOS. In this way, the UI can update each move in sequence so that the actions are graphically offset. Alternatively, the event could just throw all the information at once and the UI is just responsible for updating each move in some sequence (which could be canceled by input to skip the animation, or something). The latter case is more elegant and gives your UI more control over how it presents its information. In this sense, you may only 'need' one message sent to the UI that contains all the information about how the game state changed and what actions the game will accept-- the UI can then animate that information or not, doesn't matter to the game.
But it still seems that even in the second approach we need some kind of ordering/sequencing system either way, to, e.g. order monster moves and other things (if one monster moves first, then there is an explosion, then a second monster moves). We have to know which monster moved before which other one, so some kind of sequence specification seems unavoidable. If we just sent something saying "on this turn there was an explosion, monster A moved and monster B moved", the order is lost. This sequencing information could not be reconstructed by the UI, since it could be just the case that B moved before A as much as A moving before B, unless it is explicitly provided. We don't even know whether the explosion came between the moves of A and B or not. Unless you're suggesting to dispense with chronological accuracy, in which case sending a single giant command might work.
So how do you propose to address this?
That's what the WM is for. You make a selection to start a new character. That menu sends a message to the WM to queue up all the necessary input prompts to build the necessary parameters to pass into a new game (possibly closing itself out in the process). The WM should manage which inputs go where.
In what I have now, the menu is a pane, and panes are registered with window managers. They know their window manager, so all it does is fire up a new pane for the selected menu option and then close itself. The PC-generation wizard then is just the sequence of panes, with an object passing from one to the next that accumulates the parameters that have been input, and then at the end a new PC is generated and goes into the game.
It would seem also that the UI object would need to sense when the panes for the game are open so it can properly handle game UI events, though. Of course, since it knows about the WM and panes, then this shouldn't be a problem.
Inventory screen is typically pure UI, though you could make it so that looking at inventory is an action sent to the game which then responds with a UIEvent that provides all the information necessary to present the inventory. In this case, that action doesn't consume a turn, so the game is still waiting for an action that results in a game update.
And this is where the callback would also come in handy. The inventory screen could get an item, or it could be dismissed. If the pane is dismissed, then it doesn't call the callback that runs the turn logic.