1 //
2 // Copyright (C) 2004-2006 Jasmine Langridge, jas@jareiko.net
3 // Copyright (C) 2017 Emanuele Sorce, emanuele.sorce@hotmail.com
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //
19 
20 #pragma once
21 
22 #include <pengine.h>
23 #include <psim.h>
24 #include <unordered_map>
25 
26 
27 // Forward declaration for TriggerGame to use
28 class MainApp;
29 
30 ///
31 /// @brief This struct stores a checkpoint
32 /// @details basically just a coordinate
33 ///
34 struct CheckPoint {
35 	vec3f pt;
36 
CheckPointCheckPoint37 	CheckPoint(const vec3f &_pt) : pt(_pt) { }
38 };
39 
40 ///
41 /// @brief Holds codriver checkpoint information.
42 ///
43 struct CodriverCP
44 {
45 	// Where this checkpoint is on the map
46     vec3f pt;
47     // What the codriver should say.
48     std::string notes;
49 
CodriverCPCodriverCP50     CodriverCP(const vec3f &pt, const std::string &notes):
51         pt(pt),
52         notes(notes)
53     {
54     }
55 };
56 
57 ///
58 /// @brief The status of the race ending
59 ///
60 enum class Gamefinish {
61 	// race not finished yet
62 	not_finished,
63 	// race passed
64 	pass,
65 	// race failed
66 	fail
67 };
68 
69 ///
70 /// @brief Current state of the game
71 ///
72 enum class Gamestate {
73 	// the few seconds before start
74 	countdown,
75 	// during racing
76 	racing,
77 	// race ended
78 	finished
79 };
80 
81 ///
82 /// @brief class containing information about the race is being played
83 ///
84 class TriggerGame {
85 
86 	friend class MainApp;
87 
88 public:
89 
90     // Sets the codriver checkpoints to "ordered" (true) or "free" (false).
91     bool cdcheckpt_ordered = false;
92 
93 private:
94 	MainApp *app;
95 
96 	// simulation context
97 	PSim *sim;
98 
99 	// current state of the race
100 	Gamestate gamestate;
101 
102 	int randomseed;
103 
104 	// the vehicles
105 	std::vector<PVehicle *> vehicle;
106 
107 	// User's vehicle
108 	PVehicle *uservehicle;
109 
110 	// the terrain
111 	PTerrain *terrain;
112 
113 	// Checkpoints
114 	std::vector<CheckPoint> checkpt;
115 	// Codriver checkpoints
116 	std::vector<CodriverCP> codrivercheckpt;
117 
118 	int number_of_laps = 1;
119 
120 	// Codriver voice and sign set
121 	PCodriverVoice cdvoice;
122 	PCodriverSigns cdsigns;
123 
124 public:
125 
126     const float offroadtime_penalty_multiplier = 2.5f;
127 
128     ///
129     /// @brief Used for "real-time" counting of seconds, to scare the player.
130     /// @details it read offroad time of the user vehicle
131     ///
getOffroadTime()132     float getOffroadTime() const
133     {
134         return uservehicle->offroadtime_total + (coursetime - uservehicle->offroadtime_begin);
135     }
136 
137 private:
138 
139 	// Time passed since the race started
140 	float coursetime;
141 	// time variable used for pre and post race (i.e. Countdown)
142 	float othertime;
143 	// checkpoint time
144 	float cptime;
145 	// the time needed to win
146 	float targettime;
147 
148 	// level comment string
149 	std::string comment;
150 
151 	vec3f start_pos;
152 	quatf start_ori;
153 
154     // used to reset vehicle at last passed checkpoint
155     vec3f lastCkptPos;
156     quatf lastCkptOri;
157 
158 	// Structure that stores the current weather
159 	struct {
160 		struct {
161 			std::string texname;
162 			float scrollrate;
163 		} cloud;
164 		struct {
165 			vec3f color;
166 			float density;
167 			float density_sky;
168 		} fog;
169 		struct {
170 			float rain;
171 			float snowfall;
172 		} precip;
173 	} weather;
174 
175     ///
176     /// @brief Water information.
177     /// @todo Maybe remove defaults, used in `game.cpp`.
178     ///
179     struct
180     {
181         bool        enabled     = false;    ///< Enables the water?
182         float       height      = 0.0f;     ///< The height of the water.
183         std::string texname     = "";       ///< Custom water texture.
184         bool        useralpha   = false;    ///< Use alpha provided by user?
185         bool        fixedalpha  = false;    ///< Use the same alpha for all the water?
186         float       alpha       = 1.0f;     ///< Default user alpha value.
187     } water;
188 
189 public:
190 	std::vector<PVehicleType *> vehiclechoices;
191 
192 public:
193 	TriggerGame(MainApp *parent);
194 	~TriggerGame();
195 
resetAtCheckpoint(PVehicle * veh)196 	void resetAtCheckpoint(PVehicle *veh)
197 	{
198 		veh->doReset(lastCkptPos, lastCkptOri);
199 	}
200 
renderCodriverSigns()201 	void renderCodriverSigns()
202 	{
203 		cdsigns.render(coursetime);
204 	}
205 
206 	bool loadVehicles();
207 
208 	bool loadLevel(const std::string &filename);
209 
210 	void chooseVehicle(PVehicleType *type);
211 
212 	void tick(float delta);
213 
isFinished()214 	bool isFinished() const
215 	{
216 		return (gamestate == Gamestate::finished) && (othertime <= 0.0f);
217 	}
218 
isRacing()219 	bool isRacing() const
220 	{
221 		return gamestate == Gamestate::racing;
222 	}
223 
getFinishState()224 	Gamefinish getFinishState()
225 	{
226 		if (gamestate != Gamestate::finished)
227 			return Gamefinish::not_finished;
228 		if (coursetime + uservehicle->offroadtime_total * offroadtime_penalty_multiplier <= targettime)
229 			return Gamefinish::pass;
230 		else
231 			return Gamefinish::fail;
232 	}
233 };
234 
235 
236 #include "menu.h"
237 
238 
239 #define AS_LOAD_1           1
240 #define AS_LOAD_2           2
241 #define AS_LOAD_3           3
242 #define AS_LEVEL_SCREEN     10
243 #define AS_CHOOSE_VEHICLE   11
244 #define AS_IN_GAME          12
245 #define AS_END_SCREEN       13
246 
247 
248 
249 struct TriggerLevel {
250 	std::string filename, name, description, comment, author, targettime, targettimeshort;
251 
252 	float targettimefloat;
253 
254 	PTexture *tex_minimap = nullptr;
255 	PTexture *tex_screenshot = nullptr;
256 };
257 
258 ///
259 /// @brief an Event with his levels
260 ///
261 struct TriggerEvent {
262 	std::string filename, name, comment, author, totaltime;
263 
264 	bool locked = false;
265 	UnlockData unlocks; ///< @see `HiScore1`
266 
267 	// Note that levels are not linked to... they are
268 	// stored in the event because an event may have
269 	// "hidden" levels not otherwise available
270 
271 	std::vector<TriggerLevel> levels;
272 };
273 
274 
275 class DirtParticleSystem : public PParticleSystem {
276 public:
277 
tick(float delta)278 	void tick(float delta)
279 	{
280 		PParticleSystem::tick(delta);
281 
282 		for (unsigned int i=0; i<part.size(); i++)
283 		{
284 			PULLTOWARD(part[i].linvel, vec3f::zero(), delta * 25.0f);
285 		}
286 	}
287 };
288 
289 
290 struct RainDrop
291 {
292 	vec3f drop_pt, drop_vect;
293 	float life, prevlife;
294 };
295 
296 struct SnowFlake
297 {
298     vec3f drop_pt;
299     vec3f drop_vect;
300     float life;
301     float prevlife;
302 };
303 
304 struct UserControl {
305 	enum {
306 		TypeUnassigned,
307 		TypeKey,
308 		TypeJoyButton,
309 		TypeJoyAxis
310 	} type;
311 	union {
312 		struct {
313 			SDL_Keycode sym;
314 		} key;
315 		struct {
316 			int button;
317 		} joybutton;
318 		struct {
319 			int axis;
320 			float sign;
321 			float deadzone;
322 			float maxrange;
323 		} joyaxis; // more like half-axis, really
324 	};
325 
326 	// from 0.0 to 1.0 depending on activation level
327 	float value;
328 };
329 
330 ///
331 /// @brief this class is the whole Trigger Rally game. Create a MainApp object is the only thing main() does
332 ///
333 class MainApp : public PApp {
334 public:
335 	enum Speedunit {
336 		mph,
337 		kph
338 	};
339 
340 	enum Speedstyle {
341 		analogue,
342 		hybrid
343 	};
344 
345 	enum SnowFlakeType
346 	{
347 		point,
348 		square,
349 		textured
350 	};
351 
352 	// TODO: these shouldn't be static+public, but the simplicity is worth it for now
353 	static GLfloat    cfg_anisotropy;     ///< Anisotropic filter quality.
354 	static bool       cfg_foliage;        ///< Foliage on/off flag.
355 	static bool       cfg_roadsigns;      ///< Road signs on/off flag.
356 	static bool       cfg_weather;        ///< Weather on/off flag.
357 
358 private:
359 
360 	int appstate;
361 
362 	// TODO: use `aspect` instead of these?
363 	// TODO: type should be GLdouble instead of double
364 	double hratio; ///< Horizontal ratio.
365 	double vratio; ///< Vertical ratio.
366 
367 	UnlockData player_unlocks; ///< Unlocks for current player, see `HiScore1`.
368 
369 public:
370 
371     ///
372     /// @brief Checks if the given data was unlocked by the current player.
373     /// @param [in] udata       Unlock data to be checked.
374     /// @retval true            The player has unlocked the data.
375     /// @retval false           The player has not unlocked the data.
376     ///
isUnlockedByPlayer(const std::string & udata)377     bool isUnlockedByPlayer(const std::string &udata) const
378     {
379         return player_unlocks.count(udata) != 0;
380     }
381 
382     ///
383     /// @brief Checks if the given vehicle is locked.
384     /// @param [in] vefi        Filename of the vehicle.
385     /// @retval true            Vehicle is marked as locked.
386     /// @retval false           Vehicle is not marked as locked.
387     ///
isVehicleLocked(const std::string & vefi)388     bool isVehicleLocked(const std::string &vefi) const
389     {
390         XMLDocument xmlfile;
391         XMLElement *rootelem = PUtil::loadRootElement(xmlfile, vefi, "vehicle");
392 
393         if (rootelem == nullptr)
394         {
395             PUtil::outLog() << "Couldn't read vehicle \"" << vefi << "\"" << std::endl;
396             return false;
397         }
398 
399         const char *val = rootelem->Attribute("locked");
400 
401         if (val != nullptr && std::string(val) == "yes")
402             return true;
403 
404         return false;
405     }
406 
407 private:
408 
409 	// Config settings
410 
411 	std::string cfg_playername;
412 	bool cfg_copydefplayers;
413 
414 	int cfg_video_cx, cfg_video_cy;
415 	bool cfg_video_fullscreen;
416 
417 	float cfg_drivingassist;
418 	bool cfg_enable_sound;
419 	bool cfg_enable_codriversigns;
420 
421 	long int cfg_skip_saves;
422 
423 	/// Basic volume control.
424 	float cfg_volume_engine       = 0.33f;    ///< Engine.
425 	float cfg_volume_sfx          = 1.00f;    ///< Sound effects (wind, gear change, crash, skid, etc.)
426 	float cfg_volume_codriver     = 1.00f;    ///< Codriver voice.
427 
428 	/// Search paths for the data directory, as read from the configuration.
429 	std::list<std::string> cfg_datadirs;
430 
431 	/// Name of the codriver whose words to load.
432 	/// Must be a valid directory in /data/sounds/codriver/.
433 	std::string cfg_codrivername;
434 
435 	/// Name of the codriver icon set to load.
436 	/// Must be a valid directory in /data/textures/CodriverSigns/.
437 	std::string cfg_codriversigns;
438 
439 	/// User settings for codriver signs: position, scale, fade start time.
440 	PCodriverUserConfig cfg_codriveruserconfig;
441 
442 	Speedunit cfg_speed_unit;
443 	Speedstyle cfg_speed_style;
444 	float hud_speedo_start_deg;
445 	float hud_speedo_mps_deg_mult;
446 	float hud_speedo_mps_speed_mult;
447 
448 	SnowFlakeType cfg_snowflaketype = SnowFlakeType::point;
449 
450 	bool cfg_dirteffect = true;
451 
452 	enum Action {
453 		ActionForward,
454 		ActionBack,
455 		ActionLeft,
456 		ActionRight,
457 		ActionHandbrake,
458 		ActionRecover,
459 		ActionRecoverAtCheckpoint,
460 		ActionCamMode,
461 		ActionCamLeft,
462 		ActionCamRight,
463 		ActionShowMap,
464 		ActionShowUi,
465 		ActionShowCheckpoint,
466 		ActionPauseRace,
467 		ActionNext,
468 		ActionCount
469 	};
470 
471 	struct {
472 		std::string action_name[ActionCount];
473 		UserControl map[ActionCount];
474 	} ctrl;
475 
476 	//
477 
478 	float splashtimeout;
479 
480 	//
481 
482 	std::vector<TriggerLevel> levels;
483 	std::vector<TriggerEvent> events;
484 
485 	// for level screen
486 	Gui gui;
487 
488 public:
489 
490 	LevelState lss;
491 
492 private:
493 
494 	HISCORE1_SORT hs_sort_method = HISCORE1_SORT::BY_TOTALTIME_ASC;
495 	RaceData race_data;
496 	std::vector<TimeEntry> current_times;
497 
498 	TriggerGame *game;
499 
500 	PVehicleType *vt_tank;
501 
502 	PTexture *tex_fontSourceCodeBold,
503 			*tex_fontSourceCodeOutlined,
504 			*tex_fontSourceCodeShadowed;
505 
506 	PTexture *tex_detail,
507 			*tex_sky[1],
508 			*tex_water,
509 			*tex_waterdefault,
510 			*tex_dirt,
511 			*tex_snowflake,
512 			*tex_shadow,
513 			*tex_hud_revs,
514 			*tex_hud_revneedle,
515 			*tex_hud_life,
516 			*tex_hud_offroad,
517 			*tex_loading_screen,
518 			*tex_splash_screen,
519 			*tex_end_screen,
520 			*tex_race_no_screenshot,
521 			*tex_race_no_minimap,
522 			*tex_button_next,
523 			*tex_button_prev;
524 
525 	std::unordered_map<std::string, PTexture *> tex_codriversigns;
526 	std::unordered_map<std::string, PAudioSample *> aud_codriverwords;
527 
528 	DirtParticleSystem *psys_dirt;
529 
530 	// Tones
531 	PAudioSample *aud_engine,
532 				*aud_wind,
533 				*aud_gearchange,
534 				*aud_gravel,
535 				*aud_crash1;
536 
537 	// Audio instances
538 	PAudioInstance *audinst_engine, *audinst_wind, *audinst_gravel;
539 
540 	std::vector<PAudioInstance *> audinst;
541 
542 	float cloudscroll;
543 
544 	vec3f campos, campos_prev;
545 	quatf camori;
546 
547 	vec3f camvel;
548 
549 	float nextcpangle;
550 
551 	float cprotate;
552 
553 	int cameraview;
554 	float camera_angle;
555 	float camera_user_angle;
556 
557 	bool renderowncar; // this is determined from cameraview
558 
559 	bool showmap;
560 
561 	bool pauserace;
562 
563 	bool showui;
564 
565 	bool showcheckpoint;
566 
567 	float crashnoise_timeout;
568 
569 	std::vector<RainDrop> rain;
570 	std::vector<SnowFlake> snowfall;
571 
572 	//
573 
574 	int loadscreencount;
575 
576 	float choose_spin;
577 
578 	int choose_type;
579 
580 protected:
581 
582 	void renderWater();
583 	void renderSky(const mat44f &cammat);
584 
585 	bool startGame(const std::string &filename);
586 	void toggleSounds(bool to);
587 	void startGame2();
588 	void endGame(Gamefinish state);
589 
quitGame()590 	void quitGame()
591 	{
592 		endGame(Gamefinish::not_finished);
593 		splashtimeout = 0.0f;
594 		appstate = AS_END_SCREEN;
595 	}
596 
597 	void levelScreenAction(int action, int index);
598 	void handleLevelScreenKey(const SDL_KeyboardEvent &ke);
599 	void finishRace(Gamefinish state, float coursetime);
600 
601 public:
MainApp(const std::string & title,const std::string & name)602 	MainApp(const std::string &title, const std::string &name):
603 
604     PApp(title, name)
605 	{
606 	}
607 	//MainApp::~MainApp(); // should not have destructor, use unload
608 
getCodriverVolume()609 	float getCodriverVolume() const
610 	{
611 		return cfg_volume_codriver;
612 	}
613 
614 	void config();
615 	void load();
616 	void unload();
617 
618 	void copyDefaultPlayers() const;
619 	void loadConfig();
620 	bool loadAll();
621 	bool loadLevelsAndEvents();
622 	bool loadLevel(TriggerLevel &tl);
623 
624 	void calcScreenRatios();
625 
626 	void tick(float delta);
627 
628 	void resize();
629 	void render(float eyetranslation);
630 
631 	void renderStateLoading(float eyetranslation);
632 	void renderStateEnd(float eyetranslation);
633 	void tickStateLevel(float delta);
634 	void renderStateLevel(float eyetranslation);
635 	void tickStateChoose(float delta);
636 	void renderStateChoose(float eyetranslation);
637 	void tickStateGame(float delta);
638 	void renderStateGame(float eyetranslation);
639 
640 	void keyEvent(const SDL_KeyboardEvent &ke);
641 	void mouseMoveEvent(int dx, int dy);
642 	void cursorMoveEvent(int posx, int posy);
643 	void mouseButtonEvent(const SDL_MouseButtonEvent &mbe);
644 	void joyButtonEvent(int which, int button, bool down);
645 
getCodriverWords()646     std::unordered_map<std::string, PAudioSample *> getCodriverWords() const
647     {
648         return aud_codriverwords;
649     }
650 
getCodriverSigns()651     std::unordered_map<std::string, PTexture *> getCodriverSigns() const
652     {
653         return tex_codriversigns;
654     }
655 
getCodriverUserConfig()656     PCodriverUserConfig getCodriverUserConfig() const
657     {
658         return cfg_codriveruserconfig;
659     }
660 };
661