1 /** @file gamesession.h  Logical game session and saved session marshalling.
2  *
3  * @authors Copyright © 2014 Daniel Swanson <danij@dengine.net>
4  *
5  * @par License
6  * GPL: http://www.gnu.org/licenses/gpl.html
7  *
8  * <small>This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version. This program is distributed in the hope that it
12  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14  * Public License for more details. You should have received a copy of the GNU
15  * General Public License along with this program; if not, write to the Free
16  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17  * 02110-1301 USA</small>
18  */
19 
20 #ifndef LIBCOMMON_GAMESESSION_H
21 #define LIBCOMMON_GAMESESSION_H
22 
23 #include <de/String>
24 #include <doomsday/AbstractSession>
25 #include <doomsday/GameProfiles>
26 #include <doomsday/uri.h>
27 
28 //#include "doomsday.h"
29 //#include "acs/system.h"
30 //#include "gamerules.h"
31 
32 class GameRules;
33 
34 namespace acs { class System; }
35 
36 namespace common {
37 
38 /**
39  * Implements high level logic for the manipulation and configuration of the logical game session.
40  *
41  * An internal backing store is used to record player progress automatically, whenever the current
42  * map changes while the session is in progress. This occurs irrespective of the user's savegame
43  * preferences. Additionally, the user may configure the game so that the internal backing store
44  * is periodically (e.g., when the map changes) copied to a new "autosave" automatically.
45  *
46  * The "scope" of a continous game session progression depends on the configuration of the Episode
47  * and the maps within it. Upon leaving one map and entering another, if both are attributed to
48  * the same logical "hub" then the current state of the map is written to the backing store so
49  * that it may be reloaded later if the player(s) decide to revisit. However, if the new map is
50  * in another hub, or no hub is defined, then all saved map progress for current hub is discarded.
51  *
52  * Note that the use of hubs is not required and some games may not use them at all (e.g., DOOM).
53  *
54  * @ingroup libcommon
55  */
56 class GameSession : public AbstractSession
57 {
58 public:
59     typedef QList<de::Uri> VisitedMaps;
60 
61 public:
62     GameSession();
63     virtual ~GameSession();
64 
65     bool isSavingPossible();
66     bool isLoadingPossible();
67 
68     /**
69      * Returns the current Episode definition for the game session in progress. If the session
70      * has not yet begun then @c nullptr is returned.
71      */
72     de::Record const *episodeDef() const;
73 
74     /**
75      * Returns the current episode id for the game session in progress. If the session has not
76      * yet begun then a zero-length string is returned.
77      */
78     de::String episodeId() const;
79 
80     /**
81      * Returns the current MapGraphNode definition for the game session in progress. If the
82      * session has not yet begun then @c nullptr is returned.
83      */
84     de::Record const *mapGraphNodeDef() const;
85 
86     /**
87      * Returns the current MapInfo definition for the game session in progress. If the session
88      * has not yet begun, or no definition exists for the current map then the default definition
89      * is returned instead.
90      */
91     de::Record const &mapInfo() const;
92 
93     /**
94      * Returns the player entry point for the current map, for the game session in progress.
95      * The entry point determines where players will be reborn.
96      */
97     uint mapEntryPoint() const;
98 
99     /**
100      * Returns a list of all the maps that have been visited, for the game session in progress.
101      * @note Older versions of the saved session format did not record this information (it may
102      * be empty).
103      */
104     VisitedMaps allVisitedMaps() const;
105 
106     /**
107      * Resolves a named exit according to the map progression.
108      */
109     de::Uri mapUriForNamedExit(de::String name) const;
110 
111     /**
112      * Returns the current ruleset for the game session.
113      */
114     GameRules const &rules() const;
115 
116     /**
117      * To be called when a new game begins to effect the game rules. Note that some of the rules
118      * may be overridden here (e.g., in a networked game).
119      *
120      * @todo Prevent this outright if the game session is already in progress!
121      */
122     void applyNewRules(GameRules const &rules);
123 
124     /**
125      * Determines whether saved game progress will be restored when the current map is reloaded,
126      * according to the current game state and user configuration.
127      */
128     bool progressRestoredOnReload() const;
129 
130     /**
131      * End the game session (if in progress).
132      * @see hasBegun()
133      */
134     void end();
135 
136     /**
137      * End the game session (if in progress) and begin the title sequence.
138      * @see end(), hasBegun()
139      */
140     void endAndBeginTitle();
141 
142     /**
143      * Configure and begin a new game session. Note that a @em new session cannot @em begin if
144      * one already @ref hasBegun() (if so, the session must be ended first).
145      *
146      * @param rules          Game rules to apply.
147      * @param episodeId      Episode identifier.
148      * @param mapUri         Map identifier.
149      * @param mapEntryPoint  Map entry point number, for player reborn.
150      *
151      * @throws InProgressError if the session has already begun.
152      */
153     void begin(GameRules const &rules, de::String const &episodeId, de::Uri const &mapUri,
154                uint mapEntryPoint = 0);
155 
156     /**
157      * Reload the @em current map, automatically loading any saved progress from the backing
158      * store if @ref progressRestoredOnReload(). If no saved progress exists then the map will
159      * be in the default state.
160      */
161     void reloadMap();
162 
163     /**
164      * Leave the @em current map (automatically saving progress to the backing store) and then
165      * load up the next map specified.
166      *
167      * @param nextMapUri         Map identifier.
168      * @param nextMapEntryPoint  Map entry point number, for player reborn.
169      */
170     void leaveMap(de::Uri const &nextMapUri, uint nextMapEntryPoint = 0);
171 
172     /**
173      * Convenient method of looking up the user description of the game session in progress.
174      *
175      * @return  User description of the session if it @ref hasBegun() or a zero-length string.
176      */
177     de::String userDescription();
178 
179 public:  // Systems and data structures ------------------------------------------------------
180 
181     /**
182      * Returns the "ACS" scripting system.
183      */
184     acs::System &acsSystem();
185 
186 public:  // Saved session management ---------------------------------------------------------
187 
188     /**
189      * Save the current game state to a new @em user saved session.
190      *
191      * @param saveName         Name of the new saved session.
192      * @param userDescription  Textual description of the current game state provided either
193      *                         by the user or possibly generated automatically.
194      */
195     void save(de::String const &saveName, de::String const &userDescription);
196 
197     /**
198      * Load the game state from the @em user saved session specified.
199      *
200      * @param saveName  Name of the saved session to be loaded.
201      */
202     void load(de::String const &saveName);
203 
204     /**
205      * Makes a copy of the @em user saved session specified in /home/savegames/<gameId>
206      *
207      * @param destName    Name of the new/replaced saved session.
208      * @param sourceName  Name of the saved session to be copied.
209      */
210     void copySaved(de::String const &destName, de::String const &sourceName);
211 
212     /**
213      * Removes the @em user saved session /home/savegames/<gameId>/<@a saveName>.save
214      */
215     void removeSaved(de::String const &saveName);
216 
217     /**
218      * Convenient method of looking up the @em user description of an existing saved session.
219      *
220      * @param saveName  Name of the saved session to lookup.
221      *
222      * @return  User description of the named session or a zero-length string if not found.
223      */
224     de::String savedUserDescription(de::String const &saveName);
225 
226 public:
227     /// Returns the singleton instance.
228     static GameSession &gameSession();
229 
230     /// Register the commands and variables of this module.
231     static void consoleRegister();
232 
233 private:
234     DENG2_PRIVATE(d)
235 };
236 
237 } // namespace common
238 
239 /**
240  * Macro for conveniently accessing the common::GameSession singleton instance.
241  */
242 #define gfw_Session()    (&common::GameSession::gameSession())
243 
244 /**
245  * Returns the currently loaded game's ID.
246  */
247 de::String gfw_GameId();
248 
249 /**
250  * Returns the current game profile, or nullptr.
251  */
252 const GameProfile *gfw_GameProfile();
253 
254 #endif // LIBCOMMON_GAMESESSION_H
255