Epic Quest Land  Modes

Depending on what's going on in the game, the different events received by the game window may need to be handled in dramatically different ways:

  • The main menu handles input events by selecting menu items

  • In-game, inputs are passed to the player object

  • When viewing the inventory, a GUI system is used

  • etc.

In order to manage all these distinct system, EQL groups each way of handling events into a mode. A mode is a collection of event handlers, along with (potentially) some state to keep track of what is currently going on. The main event loop dispatches events (both real window events and synthesized events) to the current mode.

Often, it is useful to temporarily put the current mode on hold, switch to a different one, and then switch back. This is accomplished by using a stack of modes. The top of the stack is the current mode; modes beneath it are suspended. Suspended modes receive only a subset of the events they would normally get. In particular, suspended modes receive:

  • start_frame

  • redraw -- modes should use real_now(), real_elapsed_time() and real_frame() if they want to do continuous animation while suspended (now() and kin may be modified by the current mode, i.e., the mode on top of the stack).

  • resize

Suspended modes do not receive input events, or updates. When a mode is suspended its suspend handler is called; likewise, when it is resumed (is once again on top of the stack) its resume handler is called. Modes are destroyed if they are popped off the stack.

Suspended modes must not use set_time(), set_elapsed_time() or set_frame(). Control over time is solely the domain of the active mode.

  • current_mode() always returns the mode on the top of the stack. (Actually, it returns the mode that was on top of the stack, at the start of the most recent frame. That way, the current mode does not ever change during the processing of a frame.)

  • push_mode(m) pushes a new mode onto the stack. This will suspend the current mode.

  • pop_mode() pops the top mode off the stack, resuming the mode below it.

  • switch_mode is a shortcut for popping the current mode and then pushing another, effectively "switching" the current mode.

A mode can optionally run in fullscreen by having its is_fullscreen() method return true. If this is true, then any modes below it on the stack will not have their redraw handler called, as presumably nothing they drew would be visible. Note that this doesn't change anything about how the mode is drawn, it just signals to the system that modes below this one don't need to be drawn.

Lifecycle of a mode

  1. Mode creation: some modes are created from scratch every time they are needed, other modes are singletons. Singleton modes should store their state in static members, so that all instances are basically identical. Typical modes will setup some data members and maybe save their creation time/frame (time::realnow() and time::realframe()) for later use.

  2. mode::start_frame() is called at the beginning of every frame, before any other per-frame methods are called. This is mostly used to allow the mode to see the elapsed time and use it to set up local timekeeping.

  3. Event handling: any events that have been received since the last frame are dispatched to the current mode. The mode may also be paused() or unpaused() if the window loses/gains focus. If the window has been resize, the mode::resize() will be called.

  4. Mouse position: mode::set_mouse_position() is called to inform the mode of the current mouse position. This is mostly for convenience, so that modes don't need to interpret the mouse location relative to the window.

  5. Input action handling: mode::act() is called, to provide a single point for modes to collect input state and respond/dispatch it.

  6. mode::update() is called.

  7. mode::cursor() is used to get the mouse cursor to be displayed (nullptr means the cursor should be hidden)

  8. mode::redraw() is called.

  9. The screen/window is redisplayed, and then next frame starts.

For the mode stack, the events that are dispatched to all modes in a bottom-up order. (The draw event will skip any modes that are hidden by a fullscreen mode above them on the stack.)

Note that the mode destructor is virtual, so if a mode needs to clean up or do something when it is destroyed.