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