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 ¬es): 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