1. -- 
  2. -- Copyright (c) 2012 Kevin Wellwood 
  3. -- All rights reserved. 
  4. -- 
  5. -- This source code is distributed under the Modified BSD License. For terms and 
  6. -- conditions, see license.txt. 
  7. -- 
  8.  
  9. with Game_States;                       use Game_States; 
  10. with Values;                            use Values; 
  11. with Values.Associations;               use Values.Associations; 
  12. with Worlds;                            use Worlds; 
  13.  
  14. private with Ada.Containers.Indefinite_Ordered_Maps; 
  15.  
  16. package Games.Sessions is 
  17.  
  18.     -- The Game_Session object is created when a New_Game event is received, 
  19.     -- if a game session is not already in progress. It begins in the Initial 
  20.     -- state, waiting for the first Load_World event. When a Load_Level event is 
  21.     -- received, it transitions to the Loading state and loads the next world. 
  22.     -- This triggers the view to load the world's resources and signal when it's 
  23.     -- ready to play with a View_Ready event. The View_Ready event causes the 
  24.     -- game state to transition into the Playing state, where the game logic 
  25.     -- executes. The gameplay can be paused, unpaused, and interrupted by more 
  26.     -- Load_World commands between levels. When the game session ends with the 
  27.     -- End_Game event, the Game_Session object is deleted. 
  28.     -- 
  29.     --                         == State Diagram == 
  30.     -- 
  31.     --              +---------+ 
  32.     --  New_Game -> | Initial | 
  33.     --              +---------+ 
  34.     --                   | 
  35.     --                   |  Load_World 
  36.     --                  \/ 
  37.     -- Load_World   +---------+      Load_World 
  38.     --     +------> | Loading | <--------+ 
  39.     --     |        +---------+ -----+   | 
  40.     --     |              View_Ready |   | 
  41.     --     |                         |   | 
  42.     --     |                         \/  | 
  43.     -- +--------+                 +---------+ 
  44.     -- | Paused | <--- Pause ---- | Playing | 
  45.     -- +--------+ --- Unpause --> +---------+ 
  46.     --     |                             | 
  47.     --     | End_Game           End_Game | 
  48.     --     |                             | 
  49.     --     |      +-----------+          | 
  50.     --     +----> |   Ended   | <--------+ 
  51.     --            +-----------+ 
  52.  
  53.     type Session_State is (Initial, Loading, Playing, Paused, Ended); 
  54.  
  55.     type Game_Session is abstract new Object and 
  56.                                       Event_Listener and 
  57.                                       Game_State and 
  58.                                       Process with private; 
  59.     type A_Game_Session is access all Game_Session'Class; 
  60.  
  61.     -- Creates a new Game_Session object using the registered allocator. 
  62.     function Create_Game_Session( game : not null A_Game ) return A_Game_Session; 
  63.  
  64.     procedure Delete( this : in out A_Game_Session ); 
  65.  
  66.     -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  67.  
  68.     -- Notifies the game session that it is beginning a new game from the 
  69.     -- beginning. This should be called by the Game class just after object 
  70.     -- construction. One Game_Session object corresponds to one game session, 
  71.     -- played from the beginning of the game until game over or victory. The 
  72.     -- game session may be saved and loaded several times over the course of the 
  73.     -- session, but it only begins with New_Game once. 
  74.     procedure New_Game( this : not null access Game_Session'Class ); 
  75.  
  76.     -- Notifies the game session that it is ending. If 'completed' is True, the 
  77.     -- game session was ended by a loss or a victory, otherwise the game session 
  78.     -- was aborted by the player. This should be called by the Game class just 
  79.     -- before object deletion. 
  80.     procedure End_Game( this      : not null access Game_Session'Class; 
  81.                         completed : Boolean ); 
  82.  
  83.     -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  84.  
  85.     -- Returns the value of session var 'name', or Null_Value if not defined. 
  86.     function Get_Var( this : access Game_Session; 
  87.                       name : String ) return Value_Ptr; 
  88.     pragma Postcondition( Get_Var'Result /= Values.Nul ); 
  89.  
  90.     -- Sets session var 'name' to 'value'. A Game_Var_Changed event will be 
  91.     -- queued. 
  92.     procedure Set_Var( this  : access Game_Session; 
  93.                        name  : String; 
  94.                        value : Value_Ptr'Class ); 
  95.     pragma Precondition( name'Length > 0 ); 
  96.  
  97.     -- Returns the Process name of the Game_Session object. 
  98.     function Get_Process_Name( this : access Game_Session ) return String; 
  99.     pragma Postcondition( Get_Process_Name'Result'Length > 0 ); 
  100.  
  101.     -- Returns a reference to the session's active world, or null if no world 
  102.     -- is active. The world is owned by this Game_Session. 
  103.     function Get_World( this : not null access Game_Session'Class ) return A_World; 
  104.  
  105. private 
  106.  
  107.     package Worlds_Cache is 
  108.         new Ada.Containers.Indefinite_Ordered_Maps( String, A_World, "<", "=" ); 
  109.  
  110.     type Game_Session is abstract new Object and 
  111.                                       Event_Listener and 
  112.                                       Game_State and 
  113.                                       Process with 
  114.         record 
  115.             game        : A_Game := null; 
  116.             state       : Session_State := Initial; 
  117.             vars        : Assoc_Ptr; 
  118.             activeWorld : A_World := null; 
  119.             worlds      : Worlds_Cache.Map; 
  120.         end record; 
  121.  
  122.     -- Constructor for a Game_Session. 'game' owns the session. 
  123.     procedure Construct( this : access Game_Session; 
  124.                          game : not null A_Game ); 
  125.  
  126.     -- Destructor for a Game_Session. 
  127.     procedure Delete( this : in out Game_Session ); 
  128.  
  129.     -- Handles the events that the Game is registered to receive from its Corral. 
  130.     procedure Handle_Event( this : access Game_Session; 
  131.                             evt  : in out A_Event; 
  132.                             resp : out Response_Type ); 
  133.     pragma Precondition( evt /= null ); 
  134.  
  135.     -- Loads the world stored in 'filename' and sets it as the active world for 
  136.     -- game play. The view will be notified of the change via events. The game 
  137.     -- session will remain in the Loading state until the view finishes loading 
  138.     -- its resources and responds with a View_Ready event. If the game session 
  139.     -- is already in the Loading state, the command will be ignored. 
  140.     -- 
  141.     -- If world 'filename' has already been loaded in this session, the loaded 
  142.     -- world will maintain its previous state. To reset the world's state and 
  143.     -- force it to be reloaded from disk in a fresh state, set 'fresh' to True. 
  144.     -- Otherwise set it to False, to maintain the world's state. 
  145.     -- 
  146.     -- An exception will be raised if an error occurs while loading the world. 
  147.     -- The game session state and the active world (if any) will not change. 
  148.     procedure Load_World( this     : not null access Game_Session'Class; 
  149.                           filename : String; 
  150.                           fresh    : Boolean ); 
  151.  
  152.     -- Loads the world 'world' by consuming it into the Game_Session and setting 
  153.     -- it as the active world for game play. 'filename' is the name identifying 
  154.     -- the world so that it can be loaded again later. If another world with the 
  155.     -- same filename has already been loaded, it will be discarded and replaced 
  156.     -- by 'world'. If the world has been created from scratch or imported (not 
  157.     -- loaded from disk) and thus has no filename, it is acceptable to use an 
  158.     -- empty string as 'filename'. 
  159.     -- 
  160.     -- An exception will be raised if an error occurs while loading the world 
  161.     -- (ex: a world is already being loaded) and 'world' will remain untouched. 
  162.     procedure Load_World( this     : not null access Game_Session'Class; 
  163.                           filename : String; 
  164.                           world    : in out A_World ); 
  165.     pragma Precondition( world /= null ); 
  166.     pragma Postcondition( world = null ); 
  167.  
  168.     -- Pauses or resumes game play. The game can only be paused if it is in the 
  169.     -- Playing state and it can only be unpaused if it is in the Paused state. 
  170.     -- Otherwise, the command will be ignored. 
  171.     procedure Pause( this    : not null access Game_Session'Class; 
  172.                      enabled : Boolean ); 
  173.  
  174.     -- Executes one frame of logic during game play. Implement this procedure to 
  175.     -- provide behavior for the game session during game play. 
  176.     procedure Tick( this : access Game_Session; time : Tick_Time ) is null; 
  177.  
  178.     -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  179.  
  180.     -- This is called when the Game_Session receives a Load_World event to load 
  181.     -- the world in 'filename'. The default behavior is simply to call 
  182.     -- Load_World() and load 'filename' from disk every time (no previous state 
  183.     -- of the world will be maintained). Override this procedure to implement 
  184.     -- alternate behavior. 
  185.     procedure On_Load_World( this : access Game_Session; filename : String ); 
  186.  
  187.     -- This is called when the game session begins with a New_Game event. 
  188.     -- Override this procedure to implement behavior that will be executed at 
  189.     -- the beginning of the new game, to set up the initial game state, etc. 
  190.     procedure On_New_Game( this : access Game_Session ) is null; 
  191.  
  192.     -- This is called when the Game_Session receives a Pause event to pause or 
  193.     -- resume game play. The default behavior is simply to call Pause(). 
  194.     -- Override this procedure to implement alternate behavior. 
  195.     procedure On_Pause( this : access Game_Session; enabled : Boolean ); 
  196.  
  197.    -- This is called when the game session ends, either due to a game over 
  198.    -- situation, a victory, or intentionally cancelled by the player. If the 
  199.    -- game session ended due to a loss or a victory, completed will be True. 
  200.    -- Otherwise, the game session was aborted by the player and completed is 
  201.    -- False. Override this procedure to implement end-game behavior, like 
  202.    -- messaging the View to show a story sequence. 
  203.     procedure On_End_Game( this      : access Game_Session; 
  204.                            completed : Boolean ) is null; 
  205.  
  206.     ---------------------------------------------------------------------------- 
  207.  
  208.     -- An allocator for a Game_Session object. 
  209.     type Allocator is access function( game : not null A_Game ) return A_Game_Session; 
  210.  
  211.     -- Registers the allocator function used by Create_Game_Session to create an 
  212.     -- instance of the appropriate Game_Session class. This should be called 
  213.     -- at elaboration time. 
  214.     procedure Register_Allocator( allocate : not null Allocator ); 
  215.  
  216. end Games.Sessions;