--
-- Copyright (c) 2012 Kevin Wellwood
-- All rights reserved.
--
-- This source code is distributed under the Modified BSD License. For terms and
-- conditions, see license.txt.
--
with Game_States; use Game_States;
with Values; use Values;
with Values.Associations; use Values.Associations;
with Worlds; use Worlds;
private with Ada.Containers.Indefinite_Ordered_Maps;
package Games.Sessions is
-- The Game_Session object is created when a New_Game event is received,
-- if a game session is not already in progress. It begins in the Initial
-- state, waiting for the first Load_World event. When a Load_Level event is
-- received, it transitions to the Loading state and loads the next world.
-- This triggers the view to load the world's resources and signal when it's
-- ready to play with a View_Ready event. The View_Ready event causes the
-- game state to transition into the Playing state, where the game logic
-- executes. The gameplay can be paused, unpaused, and interrupted by more
-- Load_World commands between levels. When the game session ends with the
-- End_Game event, the Game_Session object is deleted.
--
-- == State Diagram ==
--
-- +---------+
-- New_Game -> | Initial |
-- +---------+
-- |
-- | Load_World
-- \/
-- Load_World +---------+ Load_World
-- +------> | Loading | <--------+
-- | +---------+ -----+ |
-- | View_Ready | |
-- | | |
-- | \/ |
-- +--------+ +---------+
-- | Paused | <--- Pause ---- | Playing |
-- +--------+ --- Unpause --> +---------+
-- | |
-- | End_Game End_Game |
-- | |
-- | +-----------+ |
-- +----> | Ended | <--------+
-- +-----------+
type Session_State is (Initial, Loading, Playing, Paused, Ended);
type Game_Session is abstract new Object and
Event_Listener and
Game_State and
Process with private;
type A_Game_Session is access all Game_Session'Class;
-- Creates a new Game_Session object using the registered allocator.
function Create_Game_Session( game : not null A_Game ) return A_Game_Session;
procedure Delete( this : in out A_Game_Session );
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- Notifies the game session that it is beginning a new game from the
-- beginning. This should be called by the Game class just after object
-- construction. One Game_Session object corresponds to one game session,
-- played from the beginning of the game until game over or victory. The
-- game session may be saved and loaded several times over the course of the
-- session, but it only begins with New_Game once.
procedure New_Game( this : not null access Game_Session'Class );
-- Notifies the game session that it is ending. If 'completed' is True, the
-- game session was ended by a loss or a victory, otherwise the game session
-- was aborted by the player. This should be called by the Game class just
-- before object deletion.
procedure End_Game( this : not null access Game_Session'Class;
completed : Boolean );
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- Returns the value of session var 'name', or Null_Value if not defined.
function Get_Var( this : access Game_Session;
name : String ) return Value_Ptr;
pragma Postcondition( Get_Var'Result /= Values.Nul );
-- Sets session var 'name' to 'value'. A Game_Var_Changed event will be
-- queued.
procedure Set_Var( this : access Game_Session;
name : String;
value : Value_Ptr'Class );
pragma Precondition( name'Length > 0 );
-- Returns the Process name of the Game_Session object.
function Get_Process_Name( this : access Game_Session ) return String;
pragma Postcondition( Get_Process_Name'Result'Length > 0 );
-- Returns a reference to the session's active world, or null if no world
-- is active. The world is owned by this Game_Session.
function Get_World( this : not null access Game_Session'Class ) return A_World;
private
package Worlds_Cache is
new Ada.Containers.Indefinite_Ordered_Maps( String, A_World, "<", "=" );
type Game_Session is abstract new Object and
Event_Listener and
Game_State and
Process with
record
game : A_Game := null;
state : Session_State := Initial;
vars : Assoc_Ptr;
activeWorld : A_World := null;
worlds : Worlds_Cache.Map;
end record;
-- Constructor for a Game_Session. 'game' owns the session.
procedure Construct( this : access Game_Session;
game : not null A_Game );
-- Destructor for a Game_Session.
procedure Delete( this : in out Game_Session );
-- Handles the events that the Game is registered to receive from its Corral.
procedure Handle_Event( this : access Game_Session;
evt : in out A_Event;
resp : out Response_Type );
pragma Precondition( evt /= null );
-- Loads the world stored in 'filename' and sets it as the active world for
-- game play. The view will be notified of the change via events. The game
-- session will remain in the Loading state until the view finishes loading
-- its resources and responds with a View_Ready event. If the game session
-- is already in the Loading state, the command will be ignored.
--
-- If world 'filename' has already been loaded in this session, the loaded
-- world will maintain its previous state. To reset the world's state and
-- force it to be reloaded from disk in a fresh state, set 'fresh' to True.
-- Otherwise set it to False, to maintain the world's state.
--
-- An exception will be raised if an error occurs while loading the world.
-- The game session state and the active world (if any) will not change.
procedure Load_World( this : not null access Game_Session'Class;
filename : String;
fresh : Boolean );
-- Loads the world 'world' by consuming it into the Game_Session and setting
-- it as the active world for game play. 'filename' is the name identifying
-- the world so that it can be loaded again later. If another world with the
-- same filename has already been loaded, it will be discarded and replaced
-- by 'world'. If the world has been created from scratch or imported (not
-- loaded from disk) and thus has no filename, it is acceptable to use an
-- empty string as 'filename'.
--
-- An exception will be raised if an error occurs while loading the world
-- (ex: a world is already being loaded) and 'world' will remain untouched.
procedure Load_World( this : not null access Game_Session'Class;
filename : String;
world : in out A_World );
pragma Precondition( world /= null );
pragma Postcondition( world = null );
-- Pauses or resumes game play. The game can only be paused if it is in the
-- Playing state and it can only be unpaused if it is in the Paused state.
-- Otherwise, the command will be ignored.
procedure Pause( this : not null access Game_Session'Class;
enabled : Boolean );
-- Executes one frame of logic during game play. Implement this procedure to
-- provide behavior for the game session during game play.
procedure Tick( this : access Game_Session; time : Tick_Time ) is null;
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- This is called when the Game_Session receives a Load_World event to load
-- the world in 'filename'. The default behavior is simply to call
-- Load_World() and load 'filename' from disk every time (no previous state
-- of the world will be maintained). Override this procedure to implement
-- alternate behavior.
procedure On_Load_World( this : access Game_Session; filename : String );
-- This is called when the game session begins with a New_Game event.
-- Override this procedure to implement behavior that will be executed at
-- the beginning of the new game, to set up the initial game state, etc.
procedure On_New_Game( this : access Game_Session ) is null;
-- This is called when the Game_Session receives a Pause event to pause or
-- resume game play. The default behavior is simply to call Pause().
-- Override this procedure to implement alternate behavior.
procedure On_Pause( this : access Game_Session; enabled : Boolean );
-- This is called when the game session ends, either due to a game over
-- situation, a victory, or intentionally cancelled by the player. If the
-- game session ended due to a loss or a victory, completed will be True.
-- Otherwise, the game session was aborted by the player and completed is
-- False. Override this procedure to implement end-game behavior, like
-- messaging the View to show a story sequence.
procedure On_End_Game( this : access Game_Session;
completed : Boolean ) is null;
----------------------------------------------------------------------------
-- An allocator for a Game_Session object.
type Allocator is access function( game : not null A_Game ) return A_Game_Session;
-- Registers the allocator function used by Create_Game_Session to create an
-- instance of the appropriate Game_Session class. This should be called
-- at elaboration time.
procedure Register_Allocator( allocate : not null Allocator );
end Games.Sessions;