1 ////////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2010 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ////////////////////////////////////////////////////////////////////////////////
9
10 /** ****************************************************************************
11 *** \file system.h
12 *** \author Tyler Olsen, roots@allacrost.org
13 *** \author Andy Gardner, chopperdave@allacrost.org
14 *** \brief Header file for system code management
15 ***
16 *** The system code handles a diverse variety of tasks including timing, threads
17 *** and translation functions.
18 ***
19 *** \note This code uses the GNU gettext library for internationalization and
20 *** localization support.
21 *** ***************************************************************************/
22
23 #ifndef __SYSTEM_HEADER__
24 #define __SYSTEM_HEADER__
25
26 #include <set>
27 #include <SDL/SDL.h>
28
29 #include "utils.h"
30 #include "defs.h"
31
32 #include "mode_manager.h"
33
34 #define NO_THREADS 0
35 #define SDL_THREADS 1
36
37 /* Set this to NO_THREADS to disable threads. Set this to SDL_THREADS to use
38 * SDL Threads. */
39 #define THREAD_TYPE SDL_THREADS
40
41 #if (THREAD_TYPE == SDL_THREADS)
42 #include <SDL/SDL_thread.h>
43 #include <SDL/SDL_mutex.h>
44 typedef SDL_Thread Thread;
45 typedef SDL_sem Semaphore;
46 #else
47 typedef int Thread;
48 typedef int Semaphore;
49 #endif
50
51 //! All calls to the system engine are wrapped in this namespace.
52 namespace hoa_system {
53
54 //! \brief The singleton pointer responsible for managing the system during game operation.
55 extern SystemEngine* SystemManager;
56
57 //! \brief Determines whether the code in the hoa_system namespace should print debug statements or not.
58 extern bool SYSTEM_DEBUG;
59
60 /** \brief A constant that represents an "infinite" number of milliseconds that can never be reached
61 *** \note This value is technically not infinite. It is the maximum value of a 32-bit
62 *** unsigned integer (2^32 - 1). This value will only be reached after ~49.7 consecutive
63 *** days of the game running.
64 **/
65 const uint32 SYSTEM_INFINITE_TIME = 0xFFFFFFFF;
66
67 /** \brief A constant to pass to any "loops" function argument in the TimerSystem class
68 *** Passing this constant to a TimerSystem object will instruct the timer to run indefinitely
69 *** and never finish.
70 **/
71 const int32 SYSTEM_TIMER_NO_LOOPS = 0;
72
73 /** \brief A constant to pass to any "loops" function argument in the TimerSystem class
74 *** Passing this constant to a TimerSystem object will instruct the timer to run indefinitely
75 *** and never finish.
76 **/
77 const int32 SYSTEM_TIMER_INFINITE_LOOP = -1;
78
79 //! \brief All of the possible states which a SystemTimer classs object may be in
80 enum SYSTEM_TIMER_STATE {
81 SYSTEM_TIMER_INVALID = -1,
82 SYSTEM_TIMER_INITIAL = 0,
83 SYSTEM_TIMER_RUNNING = 1,
84 SYSTEM_TIMER_PAUSED = 2,
85 SYSTEM_TIMER_FINISHED = 3,
86 SYSTEM_TIMER_TOTAL = 4
87 };
88
89
90 /** \brief Returns a standard string translated into the game's current language
91 *** \param text A const reference to the string that should be translated
92 *** \return Translated text in the form of a std::string
93 ***
94 *** If no translation exists in the current language for the requested string, the original string
95 *** will be returned.
96 **/
97 std::string Translate(const std::string& text);
98
99
100 /** \brief Returns a ustring translated into the game's current language
101 *** \param text A const reference to the string that should be translated
102 *** \return Translated text in the form of a hoa_utils::ustring
103 ***
104 *** \note This function is nothing more than a short-cut for typing:
105 *** MakeUnicodeString(Translate(string));
106 **/
107 hoa_utils::ustring UTranslate(const std::string& text);
108
109
110 /** ****************************************************************************
111 *** \brief A timer assistant useful for monitoring progress and processing event sequences
112 ***
113 *** This is a light-weight class for a simple timer. This class is designed specifically for
114 *** use by the various game mode classes, but it is certainly capable of being utilized just
115 *** as effectively by the engine or or other parts of the code. The operation of this class
116 *** is also integrated with the SystemEngine class, which routinely updates and manages timers.
117 *** The features of this timing mechanism include:
118 ***
119 *** - manual update of timer by a specified amount of time
120 *** - the ability to enable timers to be updated automatically
121 *** - allowance to loop for an arbitrary number of times, including an infinte number of loops
122 *** - declaring a timer to be owned by a game mode and enable the timer to be automatically paused/resumed
123 ***
124 *** When the timer is in the manual update mode, the timer must have its Update() function invoked manually
125 *** from whatever code is storing/managing the timer. In auto update mode, the timer will update automatically
126 *** whenever the SystemEngine updates itself in the main game loop. The default mode for timers is manual update.
127 ***
128 *** \note The auto pausing mechanism can only be utilized by timers that have auto update enabled and are owned
129 *** by a valid game mode. The way it works is by detecting when the active game mode (AGM) has changed and pausing
130 *** all timers which are not owned by the AGM and un-pausing all timers which are owned to the AGM.
131 *** ***************************************************************************/
132 class SystemTimer {
133 friend class SystemEngine; // For allowing SystemEngine to call the _AutoUpdate() method
134
135 public:
136 /** The no-arg constructor leaves the timer in the SYSTEM_TIMER_INVALID state.
137 *** The Initialize() method must be called before the timer can be used.
138 **/
139 SystemTimer();
140
141 /** \brief Creates and places the timer in the SYSTEM_TIMER_INITIAL state
142 *** \param duration The duration (in milliseconds) that the timer should count for
143 *** \param loops The number of times that the timer should loop for. Default value is set to no looping.
144 **/
145 SystemTimer(uint32 duration, int32 loops = 0);
146
147 virtual ~SystemTimer();
148
149 /** \brief Initializes the critical members of the system timer class
150 *** \param duration The duration (in milliseconds) that the timer should count for
151 *** \param loops The number of times that the timer should loop for. Default value is set to no looping.
152 ***
153 *** Invoking this method will instantly halt the timer and reset it to the initial state so use it with care.
154 **/
155 void Initialize(uint32 duration, int32 loops = 0);
156
157 /** \brief Enables the auto update feature for the timer
158 *** \param owner A pointer to the GameMode which owns this class. Default value is set to NULL (no owner).
159 **/
160 void EnableAutoUpdate(hoa_mode_manager::GameMode* owner = NULL);
161
162 //! \brief Disables the timer auto update feature
163 void EnableManualUpdate();
164
165 //! \brief Updates time timer with the standard game update time
166 virtual void Update();
167
168 /** \brief Updates the timer by an arbitrary amount
169 *** \param time The amount of time to increment the timer by
170 **/
171 virtual void Update(uint32 time);
172
173 //! \brief Resets the timer to its initial state
Reset()174 void Reset()
175 { if (_state != SYSTEM_TIMER_INVALID) { _state = SYSTEM_TIMER_INITIAL; _time_expired = 0; _times_completed = 0; } }
176
177 //! \brief Starts the timer from the initial state or resumes it if it is paused
Run()178 void Run()
179 { if (IsInitial() || IsPaused()) _state = SYSTEM_TIMER_RUNNING; }
180
181 //! \brief Pauses the timer if it is running
Pause()182 void Pause()
183 { if (IsRunning()) _state = SYSTEM_TIMER_PAUSED; }
184
185 //! \brief Sets the timer to the finished state
Finish()186 void Finish()
187 { _state = SYSTEM_TIMER_FINISHED; }
188
189 //! \name Timer State Checking Functions
190 //@{
IsInitial()191 bool IsInitial() const
192 { return (_state == SYSTEM_TIMER_INITIAL); }
193
IsRunning()194 bool IsRunning() const
195 { return (_state == SYSTEM_TIMER_RUNNING); }
196
IsPaused()197 bool IsPaused() const
198 { return (_state == SYSTEM_TIMER_PAUSED); }
199
IsFinished()200 bool IsFinished() const
201 { return (_state == SYSTEM_TIMER_FINISHED); }
202 //@}
203
204 /** \brief Returns the number of the current loop that the timer is on
205 *** This will always return a number greater than zero. So if the timer is on the first loop this
206 *** function will return 1, and so on.
207 **/
CurrentLoop()208 uint32 CurrentLoop() const
209 { return (_times_completed + 1); }
210
211 //! \brief Returns the time remaining for the current loop to end
TimeLeft()212 uint32 TimeLeft() const
213 { return (_duration - _time_expired); }
214
215 /** \brief Returns a float representing the percent completion for the current loop
216 *** \return A float with a value between 0.0f and 1.0f
217 *** \note This function is only concered with the percent completion for the current loop.
218 *** The number of loops is not taken into account at all.
219 ***
220 *** This method will return 1.0f if the state is SYSTEM_TIMER_FINISHED or 0.0f if the state
221 *** is anything other than SYSTEM_TIMER_RUNNING or SYSTEM_TIMER_PAUSED. The number of loops
222 **/
223 float PercentComplete() const;
224
225 /** \name Member Set Access Functions
226 *** \note <b>Only</b> call these methods when the timer is in its initial state. Trying to set
227 *** any of these members when in any other state will yield no change and print a warning message.
228 **/
229 //@{
230 void SetDuration(uint32 duration);
231
232 void SetNumberLoops(int32 loops);
233
234 void SetModeOwner(hoa_mode_manager::GameMode* owner);
235 //@}
236
237 //! \name Class Member Accessor Methods
238 //@{
GetState()239 SYSTEM_TIMER_STATE GetState() const
240 { return _state; }
241
GetDuration()242 uint32 GetDuration() const
243 { return _duration; }
244
GetNumberLoops()245 int32 GetNumberLoops() const
246 { return _number_loops; }
247
IsAutoUpdate()248 bool IsAutoUpdate() const
249 { return _auto_update; }
250
GetModeOwner()251 hoa_mode_manager::GameMode* GetModeOwner() const
252 { return _mode_owner; }
253
GetTimeExpired()254 uint32 GetTimeExpired() const
255 { return _time_expired; }
256
GetTimesCompleted()257 uint32 GetTimesCompleted() const
258 { return _times_completed; }
259 //@}
260
261 protected:
262 //! \brief Maintains the current state of the timer (initial, running, paused, or finished)
263 SYSTEM_TIMER_STATE _state;
264
265 //! \brief When true the timer will automatically update itself
266 bool _auto_update;
267
268 //! \brief The duration (in milliseconds) that the timer should run for
269 uint32 _duration;
270
271 //! \brief The number of loops the timer should run for. -1 indicates infinite looping.
272 int32 _number_loops;
273
274 //! \brief A pointer to the game mode object which owns this timer, or NULL if it is unowned
275 hoa_mode_manager::GameMode* _mode_owner;
276
277 //! \brief The amount of time that has expired on the current timer loop (counts up from 0 to _duration)
278 uint32 _time_expired;
279
280 //! \brief Incremented by one each time the timer reaches the finished state
281 uint32 _times_completed;
282
283 /** \brief Updates the timer if it is running and has auto updating enabled
284 *** This method can only be invoked by the SystemEngine class.
285 **/
286 virtual void _AutoUpdate();
287
288 /** \brief Performs the actual update of the class members
289 *** \param amount The amount of time to update the timer by
290 ***
291 *** The function contains the core logic of performing the update for the _time_expired and
292 *** _times_completed members as well as setting the _state member to SYSTEM_TIMER_FINISHED
293 *** when the timer has completed all of its loops. This is a helper function to the Update()
294 *** and _AutoUpdate() methods, who should perform all appropriate checking of timer state
295 *** before calling this method. The method intentionally does not do any state or error-checking
296 *** by itself; It simply updates the timer without complaint.
297 **/
298 void _UpdateTimer(uint32 amount);
299 }; // class SystemTimer
300
301
302 /** ****************************************************************************
303 *** \brief Engine class that manages system information and functions
304 ***
305 *** This is somewhat of a "miscellaneous" game engine class that manages constructs
306 *** that don't really fit in with any other engine component. Perhaps the most
307 *** important task that this engine component handles is that of timing.
308 ***
309 *** \note This class is a singleton.
310 *** ***************************************************************************/
311 class SystemEngine : public hoa_utils::Singleton<SystemEngine> {
312 friend class hoa_utils::Singleton<SystemEngine>;
313
314 public:
315 ~SystemEngine();
316
317 bool SingletonInitialize();
318
319 /** \brief Initializes the timers used in the game
320 *** This function should only be called <b>once</b> in main.cpp, just before the main game loop begins.
321 **/
322 void InitializeTimers();
323
324 /** \brief Initializes the game update timer
325 *** This function should typically only be called when the active game mode is changed. This ensures that
326 *** the active game mode's execution begins with only 1 millisecond of time expired instead of several.
327 **/
InitializeUpdateTimer()328 void InitializeUpdateTimer()
329 { _last_update = SDL_GetTicks(); _update_time = 1; }
330
331 /** \brief Adds a timer to the set system timers for auto updating
332 *** \param timer A pointer to the timer to add
333 ***
334 *** If the timer object does not have the auto update feature enabled, a warning will be printed and the
335 *** timer will not be added.
336 **/
337 void AddAutoTimer(SystemTimer* timer);
338
339 /** \brief Removes a timer to the set system timers for auto updating
340 *** \param timer A pointer to the timer to add
341 ***
342 *** If the timer object does not have the auto update feature enabled, a warning will be printed but it
343 *** will still attempt to remove the timer.
344 **/
345 void RemoveAutoTimer(SystemTimer* timer);
346
347 /** \brief Updates the game timer variables.
348 *** This function should only be called <b>once</b> for each cycle through the main game loop. Since
349 *** it is called inside the loop in main.cpp, you should have no reason to call this function anywhere
350 *** else.
351 **/
352 void UpdateTimers();
353
354 /** \brief Checks all system timers for whether they should be paused or resumed
355 *** This function is typically called whenever the ModeEngine class has changed the active game mode.
356 *** When this is done, all system timers that are owned by the active game mode are resumed, all timers with
357 *** a different owner are paused, and all timers with no owner are ignored.
358 **/
359 void ExamineSystemTimers();
360
361 /** \brief Retrieves the amount of time that the game should be updated by for time-based movement.
362 *** This function should \b only be called in the main game loop, located in main.cpp.
363 *** \return The number of milliseconds that have transpired since the last update.
364 ***
365 *** \note There's a chance we could get errors in other parts of the program code if the
366 *** value returned by this function is zero. We can prevent this if we always make sure the
367 *** function returns at least one, but I'm not sure there exists a computer fast enough
368 *** that we have to worry about it.
369 **/
GetUpdateTime()370 uint32 GetUpdateTime() const
371 { return _update_time; }
372
373 /** \brief Sets the play time of a game instance
374 *** \param h The amount of hours to set.
375 *** \param m The amount of minutes to set.
376 *** \param s The amount of seconds to set.
377 ***
378 *** This function is meant to be called whenever the user loads a saved game.
379 **/
SetPlayTime(const uint8 h,const uint8 m,const uint8 s)380 void SetPlayTime(const uint8 h, const uint8 m, const uint8 s)
381 { _hours_played = h; _minutes_played = m; _seconds_played = s; _milliseconds_played = 0; }
382
383 /** \brief Functions for retrieving the play time.
384 *** \return The number of hours, minutes, or seconds of play time.
385 **/
386 //@{
GetPlayHours()387 uint8 GetPlayHours() const
388 { return _hours_played; }
389
GetPlayMinutes()390 uint8 GetPlayMinutes() const
391 { return _minutes_played; }
392
GetPlaySeconds()393 uint8 GetPlaySeconds() const
394 { return _seconds_played; }
395 //@}
396
397 /** \brief Used to determine what language the game is running in.
398 *** \return The language that the game is running in.
399 **/
GetLanguage()400 std::string GetLanguage() const
401 { return _language; }
402
403 /** \brief Sets the language that the game should use.
404 *** \param lang A two-character string representing the language to execute the game in
405 **/
406 void SetLanguage(std::string lang);
407
408 /** \brief Determines whether the user is done with the game.
409 *** \return False if the user would like to exit the game.
410 **/
NotDone()411 bool NotDone() const
412 { return _not_done; }
413
414 /** \brief The function to call to initialize the exit process of the game.
415 *** \note The game will exit the main loop once it reaches the end of its current iteration
416 **/
ExitGame()417 void ExitGame()
418 { _not_done = false; }
419
420 //! Threading classes
421 template <class T> Thread* SpawnThread(void (T::*)(), T *);
422 void WaitForThread(Thread* thread);
423
424 void LockThread(Semaphore *);
425 void UnlockThread(Semaphore *);
426 Semaphore * CreateSemaphore(int max);
427 void DestroySemaphore(Semaphore *);
428
429
430 private:
431 SystemEngine();
432
433 //! \brief The last time that the UpdateTimers function was called, in milliseconds.
434 uint32 _last_update;
435
436 //! \brief The number of milliseconds that have transpired on the last timer update.
437 uint32 _update_time;
438
439 /** \name Play time members
440 *** \brief Timers that retain the total amount of time that the user has been playing
441 *** When the player starts a new game or loads an existing game, these timers are reset.
442 **/
443 //@{
444 uint8 _hours_played;
445 uint8 _minutes_played;
446 uint8 _seconds_played;
447 uint16 _milliseconds_played; //!< \note Milliseconds are not retained when saving or loading a saved game file.
448 //@}
449
450 //! \brief When this member is set to false, the program will exit.
451 bool _not_done;
452
453 //! \brief The identification string that determines what language the game is running in
454 std::string _language;
455
456 /** \brief A set container for all SystemTimer objects that have automatic updating enabled
457 *** The timers in this container are updated on each call to UpdateTimers().
458 **/
459 std::set<SystemTimer*> _auto_system_timers;
460 }; // class SystemEngine : public hoa_utils::Singleton<SystemEngine>
461
462
463
464 template <class T> struct generic_class_func_info
465 {
SpawnThread_Intermediategeneric_class_func_info466 static int SpawnThread_Intermediate(void* vptr) {
467 ((((generic_class_func_info <T> *) vptr)->myclass)->*(((generic_class_func_info <T> *) vptr)->func))();
468 return 0;
469 }
470
471 T* myclass;
472 void (T::*func)();
473 };
474
475
476
SpawnThread(void (T::* func)(),T * myclass)477 template <class T> Thread* SystemEngine::SpawnThread(void (T::*func)(), T* myclass) {
478 #if (THREAD_TYPE == SDL_THREADS)
479 Thread * thread;
480 static generic_class_func_info <T> gen;
481 gen.func = func;
482 gen.myclass = myclass;
483
484 // Winter Knight: There is a potential, but unlikely race condition here.
485 // gen may be overwritten prematurely if this function, SpawnThread, gets
486 // called a second time before SpawnThread_Intermediate calls myclass->*func
487 // This will result in a segfault.
488 thread = SDL_CreateThread(gen.SpawnThread_Intermediate, &gen);
489 if (thread == NULL) {
490 PRINT_ERROR << "Unable to create thread: " << SDL_GetError() << std::endl;
491 return NULL;
492 }
493 return thread;
494 #elif (THREAD_TYPE == NO_THREADS)
495 (myclass->*func)();
496 return 1;
497 #else
498 PRINT_ERROR << "Invalid THREAD_TYPE." << std::endl;
499 return 0;
500 #endif
501 }
502
503 } // namepsace hoa_system
504
505 #endif // __SYSTEM_HEADER__
506